mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 05:27:37 +00:00
Fix autocomplete trigger after dot
This commit is contained in:
1 parent
d63a1b8d4c
commit
4e78ffb024
3 files changed
+80
-25
No files matched your search
+38
-14
@@ -179,18 +179,27 @@ public class AutocompleteManager {
|
||||
|
||||
// Check if we're typing an identifier
|
||||
if (Character.isJavaIdentifierPart(c)) {
|
||||
// GuiScriptTextArea passes a cursor position captured BEFORE insertion while `text`
|
||||
// is already the post-insert text. When this happens, cursorPosition points at the
|
||||
// newly inserted character, not the caret-after-insert. Normalize to a caret position
|
||||
// for prefix extraction/update.
|
||||
int effectiveCursor = cursorPosition;
|
||||
if (effectiveCursor >= 0 && effectiveCursor < text.length() && text.charAt(effectiveCursor) == c) {
|
||||
effectiveCursor = effectiveCursor + 1;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
// Update existing autocomplete
|
||||
updatePrefix(text, cursorPosition);
|
||||
updatePrefix(text, effectiveCursor);
|
||||
} else if (Character.isJavaIdentifierStart(c)) {
|
||||
// Check if we're after a dot with whitespace (e.g., "obj. |" where | is cursor)
|
||||
int dotPos = findDotBeforeWhitespace(text, cursorPosition - 1);
|
||||
int dotPos = findDotBeforeWhitespace(text, effectiveCursor - 1);
|
||||
if (dotPos >= 0) {
|
||||
// We're typing after a dot (with possible whitespace), trigger member access
|
||||
triggerAfterDot(text, cursorPosition);
|
||||
triggerAfterDot(text, effectiveCursor);
|
||||
} else {
|
||||
// Potentially start new autocomplete
|
||||
maybeStartAutocomplete(text, cursorPosition, false);
|
||||
maybeStartAutocomplete(text, effectiveCursor, false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -283,29 +292,39 @@ public class AutocompleteManager {
|
||||
* Trigger autocomplete after a dot is typed.
|
||||
*/
|
||||
private void triggerAfterDot(String text, int cursorPosition) {
|
||||
// Find the receiver expression before the dot
|
||||
// First check if immediately before cursor
|
||||
int dotPos = cursorPosition - 1;
|
||||
if (dotPos < 0 || text.charAt(dotPos) != '.') {
|
||||
// Look backwards skipping whitespace to find dot
|
||||
dotPos = findDotBeforeWhitespace(text, cursorPosition - 1);
|
||||
// Normalize cursor semantics.
|
||||
// Some callers pass a pre-insert cursor index (points at '.') even though the caret
|
||||
// is visually after the inserted '.' in the current text.
|
||||
int caretPos = Math.max(0, Math.min(cursorPosition, text.length()));
|
||||
|
||||
int dotPos = -1;
|
||||
if (caretPos > 0 && caretPos <= text.length() && text.charAt(caretPos - 1) == '.') {
|
||||
// Caret-after-dot case
|
||||
dotPos = caretPos - 1;
|
||||
} else if (caretPos >= 0 && caretPos < text.length() && text.charAt(caretPos) == '.') {
|
||||
// Pre-insert cursor case: cursorPosition points at the dot itself
|
||||
dotPos = caretPos;
|
||||
caretPos = Math.min(caretPos + 1, text.length());
|
||||
} else {
|
||||
// Look backwards skipping whitespace/identifier to find dot
|
||||
dotPos = findDotBeforeWhitespace(text, caretPos - 1);
|
||||
}
|
||||
|
||||
if (dotPos < 0) return;
|
||||
|
||||
String receiverExpr = findReceiverExpression(text, dotPos);
|
||||
String prefix = findCurrentWord(text, cursorPosition);
|
||||
String prefix = findCurrentWord(text, caretPos);
|
||||
|
||||
// Find where the prefix actually starts (after dot + any whitespace)
|
||||
int prefixStart = dotPos + 1;
|
||||
while (prefixStart < cursorPosition && Character.isWhitespace(text.charAt(prefixStart))) {
|
||||
while (prefixStart < caretPos && Character.isWhitespace(text.charAt(prefixStart))) {
|
||||
prefixStart++;
|
||||
}
|
||||
|
||||
prefixStartPosition = prefixStart;
|
||||
currentPrefix = prefix;
|
||||
|
||||
showSuggestions(text, cursorPosition, prefix, prefixStartPosition, true, receiverExpr);
|
||||
showSuggestions(text, caretPos, prefix, prefixStartPosition, true, receiverExpr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,7 +398,12 @@ public class AutocompleteManager {
|
||||
|
||||
// Resolve receiver type if member access
|
||||
if (isMemberAccess && receiverExpr != null) {
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, prefixStart);
|
||||
// Use a position inside the receiver/scope for resolution.
|
||||
// `prefixStart` can land on end-exclusive boundaries (e.g., right at bodyEnd),
|
||||
// causing findInnermostScopeAt(prefixStart) to return null.
|
||||
int dotPos = findDotBeforeWhitespace(text, prefixStart - 1);
|
||||
int resolvePos = dotPos >= 0 ? dotPos : prefixStart;
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, resolvePos);
|
||||
if (receiverType != null && receiverType.isResolved()) {
|
||||
currentReceiverFullName = receiverType.getFullName();
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public class JSAutocompleteProvider extends JavaAutocompleteProvider {
|
||||
}
|
||||
|
||||
// Use ScriptDocument's resolveExpressionType - handles both Java and JS
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, context.prefixStart);
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, getMemberAccessResolvePosition(context));
|
||||
if (receiverType == null || !receiverType.isResolved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
+41
-10
@@ -43,13 +43,14 @@ public class JavaAutocompleteProvider implements AutocompleteProvider {
|
||||
String ownerFullName = null;
|
||||
boolean isStaticContext = false;
|
||||
if (context.isMemberAccess && context.receiverExpression != null) {
|
||||
int resolvePos = getMemberAccessResolvePosition(context);
|
||||
TypeInfo receiverType = document.resolveExpressionType(
|
||||
context.receiverExpression, context.prefixStart);
|
||||
context.receiverExpression, resolvePos);
|
||||
if (receiverType != null && receiverType.isResolved()) {
|
||||
ownerFullName = receiverType.getFullName();
|
||||
}
|
||||
// Check if accessing through a class (static context) vs instance
|
||||
isStaticContext = isStaticAccess(context.receiverExpression, context.prefixStart);
|
||||
isStaticContext = isStaticAccess(context.receiverExpression, resolvePos);
|
||||
}
|
||||
|
||||
if (context.isMemberAccess) {
|
||||
@@ -79,14 +80,14 @@ public class JavaAutocompleteProvider implements AutocompleteProvider {
|
||||
}
|
||||
|
||||
// Resolve the type of the receiver expression
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, context.prefixStart);
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, getMemberAccessResolvePosition(context));
|
||||
|
||||
if (receiverType == null || !receiverType.isResolved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if this is a static context (accessing a class type)
|
||||
boolean isStaticContext = isStaticAccess(receiverExpr, context.prefixStart);
|
||||
boolean isStaticContext = isStaticAccess(receiverExpr, getMemberAccessResolvePosition(context));
|
||||
|
||||
Class<?> clazz = receiverType.getJavaClass();
|
||||
if (clazz == null) {
|
||||
@@ -129,6 +130,36 @@ public class JavaAutocompleteProvider implements AutocompleteProvider {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a stable position for resolving member-access receiver types.
|
||||
*
|
||||
* Using `prefixStart` can land on end-exclusive scope boundaries (position == bodyEnd)
|
||||
* depending on caller cursor semantics and surrounding syntax, causing scope lookups
|
||||
* to fail. For member access we prefer the dot position (or nearest non-whitespace)
|
||||
* immediately before prefixStart.
|
||||
*/
|
||||
protected int getMemberAccessResolvePosition(Context context) {
|
||||
if (context == null || context.text == null || context.text.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pos = Math.max(0, Math.min(context.prefixStart, context.text.length()));
|
||||
int i = Math.min(pos - 1, context.text.length() - 1);
|
||||
|
||||
// Skip whitespace backwards
|
||||
while (i >= 0 && Character.isWhitespace(context.text.charAt(i))) {
|
||||
i--;
|
||||
}
|
||||
|
||||
// Prefer the dot index if present
|
||||
if (i >= 0 && context.text.charAt(i) == '.') {
|
||||
return i;
|
||||
}
|
||||
|
||||
// Fallback: use the provided prefixStart (caret context)
|
||||
return Math.max(0, Math.min(context.prefixStart, context.text.length()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add members from a script-defined type.
|
||||
@@ -184,10 +215,10 @@ public class JavaAutocompleteProvider implements AutocompleteProvider {
|
||||
*/
|
||||
protected void addScopeSuggestions(Context context, List<AutocompleteItem> items) {
|
||||
int pos = context.cursorPosition;
|
||||
|
||||
|
||||
// Find containing method
|
||||
MethodInfo containingMethod = document.findContainingMethod(pos);
|
||||
|
||||
|
||||
// Add local variables
|
||||
if (containingMethod != null) {
|
||||
Map<String, FieldInfo> locals = document.getLocalsForMethod(containingMethod);
|
||||
@@ -198,20 +229,20 @@ public class JavaAutocompleteProvider implements AutocompleteProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 enclosing type fields (find via script types map)
|
||||
ScriptTypeInfo enclosingType = findEnclosingType(pos);
|
||||
if (enclosingType != null) {
|
||||
@@ -220,7 +251,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider {
|
||||
items.add(AutocompleteItem.fromField(field));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add methods from enclosing type
|
||||
for (List<MethodInfo> overloads : enclosingType.getMethods().values()) {
|
||||
for (MethodInfo method : overloads) {
|
||||
|
||||
Reference in new issue
Block a user