mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 18:57:32 +00:00
Fixed imports not resolving
if wrapped to new line
This commit is contained in:
1 parent
01e2c354d5
commit
83f45fe6b8
3 files changed
+121
-72
No files matched your search
@@ -62,24 +62,27 @@ public class ClassPathFinder {
|
||||
public final String classPortion; // The class part (e.g., "List" or "IOverlay.ColorType")
|
||||
public final List<ClassSegment> classSegments; // Each class/inner-class segment with its type
|
||||
public final int invalidStartOffset; // Character offset where invalid portion starts (-1 if all valid or all invalid)
|
||||
public final int packageSegmentCount; // Number of dot-separated identifier tokens that belong to the package portion
|
||||
|
||||
private ResolveResult(boolean found, ClassInfo classInfo, String packagePortion,
|
||||
String classPortion, List<ClassSegment> classSegments, int invalidStartOffset) {
|
||||
String classPortion, List<ClassSegment> classSegments, int invalidStartOffset,
|
||||
int packageSegmentCount) {
|
||||
this.found = found;
|
||||
this.classInfo = classInfo;
|
||||
this.packagePortion = packagePortion;
|
||||
this.classPortion = classPortion;
|
||||
this.classSegments = classSegments != null ? classSegments : Collections.emptyList();
|
||||
this.invalidStartOffset = invalidStartOffset;
|
||||
this.packageSegmentCount = packageSegmentCount;
|
||||
}
|
||||
|
||||
public static ResolveResult notFound(String packagePortion, String classPortion, int invalidStartOffset) {
|
||||
return new ResolveResult(false, null, packagePortion, classPortion, null, invalidStartOffset);
|
||||
public static ResolveResult notFound(String packagePortion, String classPortion, int invalidStartOffset, int packageSegmentCount) {
|
||||
return new ResolveResult(false, null, packagePortion, classPortion, null, invalidStartOffset, packageSegmentCount);
|
||||
}
|
||||
|
||||
public static ResolveResult found(ClassInfo info, String packagePortion,
|
||||
String classPortion, List<ClassSegment> segments) {
|
||||
return new ResolveResult(true, info, packagePortion, classPortion, segments, -1);
|
||||
String classPortion, List<ClassSegment> segments, int packageSegmentCount) {
|
||||
return new ResolveResult(true, info, packagePortion, classPortion, segments, -1, packageSegmentCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,15 +116,20 @@ public class ClassPathFinder {
|
||||
*/
|
||||
public ResolveResult resolve(String importPath) {
|
||||
if (importPath == null || importPath.isEmpty()) {
|
||||
return ResolveResult.notFound("", "", -1);
|
||||
return ResolveResult.notFound("", "", -1, 0);
|
||||
}
|
||||
|
||||
// Normalize whitespace/newlines around dots so wrapped imports like
|
||||
// "kamkeel.npcdbc.api.client.overlay.IOverlay.\n TextureFunction" become
|
||||
// "kamkeel.npcdbc.api.client.overlay.IOverlay.TextureFunction" before resolving.
|
||||
String normalizedPath = importPath.replaceAll("\\s*\\.\\s*", ".").trim();
|
||||
|
||||
// Handle trailing dot (user is still typing a package path like "java.util.")
|
||||
boolean trailingDot = importPath.endsWith(".");
|
||||
String pathToResolve = trailingDot ? importPath.substring(0, importPath.length() - 1) : importPath;
|
||||
boolean trailingDot = normalizedPath.endsWith(".");
|
||||
String pathToResolve = trailingDot ? normalizedPath.substring(0, normalizedPath.length() - 1) : normalizedPath;
|
||||
|
||||
if (pathToResolve.isEmpty()) {
|
||||
return ResolveResult.notFound("", "", -1);
|
||||
return ResolveResult.notFound("", "", -1, 0);
|
||||
}
|
||||
|
||||
String[] segments = pathToResolve.split("\\.");
|
||||
@@ -136,18 +144,18 @@ public class ClassPathFinder {
|
||||
int longestValid = findLongestValidPackagePrefixIndex(segments);
|
||||
if (longestValid == n) {
|
||||
// Entire path is a valid package (or cached as valid)
|
||||
return ResolveResult.notFound(pathToResolve, "", -1);
|
||||
return ResolveResult.notFound(pathToResolve, "", -1, n);
|
||||
}
|
||||
|
||||
if (longestValid > 0) {
|
||||
String validPrefix = buildPackagePortion(segments, longestValid);
|
||||
String invalidRemainder = buildClassPortion(segments, longestValid, n);
|
||||
int invalidOffset = validPrefix.length() + 1; // after the dot
|
||||
return ResolveResult.notFound(validPrefix, invalidRemainder, invalidOffset);
|
||||
return ResolveResult.notFound(validPrefix, invalidRemainder, invalidOffset, longestValid);
|
||||
}
|
||||
|
||||
// Nothing validated
|
||||
return ResolveResult.notFound("", pathToResolve, 0);
|
||||
return ResolveResult.notFound("", pathToResolve, 0, 0);
|
||||
}
|
||||
|
||||
// Trailing dot is handled above for lowercase-only paths; for mixed-case paths
|
||||
@@ -180,15 +188,15 @@ public class ClassPathFinder {
|
||||
// Calculate where the invalid portion starts (after valid class + dot)
|
||||
int validEndOffset = packagePortion.length() + (packagePortion.isEmpty() ? 0 : 1) + validClassPortion.length() + 1;
|
||||
return new ResolveResult(false, info, packagePortion,
|
||||
fullClassPortion, classSegments, validEndOffset);
|
||||
fullClassPortion, classSegments, validEndOffset, classStartIdx);
|
||||
}
|
||||
|
||||
// Full resolution successful
|
||||
List<ClassSegment> classSegments = buildClassSegments(segments, classStartIdx, n);
|
||||
return ResolveResult.found(
|
||||
return ResolveResult.found(
|
||||
new ClassInfo(info.resolvedName, info.type, packagePortion.length()),
|
||||
packagePortion, validClassPortion, classSegments
|
||||
);
|
||||
packagePortion, validClassPortion, classSegments, classStartIdx
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,22 +216,22 @@ public class ClassPathFinder {
|
||||
// Found a divergence from a known valid package
|
||||
String validPrefix = buildPackagePortion(segments, typoIndex);
|
||||
int invalidOffset = validPrefix.isEmpty() ? 0 : validPrefix.length() + 1;
|
||||
return ResolveResult.notFound(validPrefix, buildClassPortion(segments, typoIndex, n), invalidOffset);
|
||||
return ResolveResult.notFound(validPrefix, buildClassPortion(segments, typoIndex, n), invalidOffset, typoIndex);
|
||||
}
|
||||
|
||||
// No typo detected - assume package is valid, only class portion is invalid
|
||||
int invalidOffset = packagePortion.length() + 1; // After the package dot
|
||||
return ResolveResult.notFound(packagePortion, classPortion, invalidOffset);
|
||||
return ResolveResult.notFound(packagePortion, classPortion, invalidOffset, firstUpperIndex);
|
||||
|
||||
} else if (firstUpperIndex == 0) {
|
||||
// Entire path starts with uppercase (e.g., "String" or "MyClass.Inner")
|
||||
// Treat as class with no package
|
||||
return ResolveResult.notFound("", pathToResolve, 0);
|
||||
return ResolveResult.notFound("", pathToResolve, 0, 0);
|
||||
|
||||
} else {
|
||||
// All lowercase, no uppercase found - entire path is package-like
|
||||
// If trailing dot was handled above, this is just a package without class
|
||||
return ResolveResult.notFound(pathToResolve, "", -1);
|
||||
return ResolveResult.notFound(pathToResolve, "", -1, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ public class IndentHelper {
|
||||
settings.wrapComments = true;
|
||||
// Use 75% of viewport width as max line length for some margin
|
||||
int charWidth = 6; // Approximate character width
|
||||
settings.maxLineLength = Math.max(60, (int)(viewportWidth * 0.75 / charWidth));
|
||||
settings.maxLineLength = Math.max(60, (int)(viewportWidth * 0.8f / charWidth));
|
||||
}
|
||||
|
||||
String formatted = helper.format(indented);
|
||||
|
||||
@@ -37,9 +37,11 @@ public class JavaTextContainer extends TextContainer {
|
||||
public static final Pattern NUMBER = Pattern.compile(
|
||||
"\\b-?(?:0[xX][\\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?(?:[fFbBdDlLsS])?|NaN|null|Infinity|true|false)\\b");
|
||||
|
||||
// Import statement: import [static] package.path.ClassName [.*]; (semicolon optional for live typing)
|
||||
public static final Pattern IMPORT = Pattern.compile(
|
||||
"(?m)\\bimport\\s+(?:static\\s+)?([a-zA-Z_][a-zA-Z0-9_.]*?)(?:\\s*\\.\\s*\\*)?\\s*(?:;|$)");
|
||||
// Import statement: import [static] package.path.ClassName [.*]; (semicolon optional for live typing)
|
||||
// Allows optional whitespace (including newlines) around the dots so wrapped imports match.
|
||||
// Match imports while allowing optional whitespace/newlines around dots.
|
||||
public static final Pattern IMPORT = Pattern.compile(
|
||||
"(?m)\\bimport\\s+(?:static\\s+)?([A-Za-z_][A-Za-z0-9_]*(?:\\s*\\.\\s*[A-Za-z_][A-Za-z0-9_]*)*)(?:\\s*\\.\\s*\\*)?\\s*(?:;|$)");
|
||||
|
||||
// ClassPathFinder for resolving imports and determining class types
|
||||
private final ClassPathFinder classPathFinder = new ClassPathFinder();
|
||||
@@ -51,6 +53,26 @@ public class JavaTextContainer extends TextContainer {
|
||||
super(text);
|
||||
}
|
||||
|
||||
// Map an offset into the normalized path (tokens joined with '.') back to
|
||||
// an absolute index in the original substring `orig` (absolute positions start at pathStart).
|
||||
// The normalization removes whitespace/newlines around dots; this function
|
||||
// advances a normalized counter when it encounters identifier chars or dots
|
||||
// and returns the corresponding original index (absolute).
|
||||
private int mapNormalizedOffsetToOriginal(String orig, int normalizedOffset, int pathStart) {
|
||||
if (orig == null || orig.isEmpty()) return pathStart;
|
||||
int norm = 0;
|
||||
for (int i = 0; i < orig.length(); i++) {
|
||||
char c = orig.charAt(i);
|
||||
if (Character.isLetterOrDigit(c) || c == '_' || c == '.') {
|
||||
norm++;
|
||||
}
|
||||
if (norm >= normalizedOffset) {
|
||||
return pathStart + i;
|
||||
}
|
||||
}
|
||||
return pathStart + orig.length();
|
||||
}
|
||||
|
||||
public void init(String text,int width, int height) {
|
||||
this.text = text == null ? "" : text.replaceAll("\\r?\\n|\\r", "\n");
|
||||
lines.clear();
|
||||
@@ -519,75 +541,94 @@ public class JavaTextContainer extends TextContainer {
|
||||
// Use ClassPathFinder to resolve the import
|
||||
ClassPathFinder.ResolveResult result = classPathFinder.resolve(fullPath);
|
||||
|
||||
String packagePortion = result.packagePortion;
|
||||
String classPortion = result.classPortion;
|
||||
boolean hasPackage = packagePortion != null && !packagePortion.isEmpty();
|
||||
boolean hasClass = classPortion != null && !classPortion.isEmpty();
|
||||
// Tokenize the original matched substring so we can map normalized segments
|
||||
String orig = text.substring(pathStart, pathEnd);
|
||||
java.util.List<String> tokens = new java.util.ArrayList<>();
|
||||
java.util.List<Integer> tokenStarts = new java.util.ArrayList<>();
|
||||
java.util.List<Integer> tokenEnds = new java.util.ArrayList<>();
|
||||
Matcher idm = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*").matcher(orig);
|
||||
while (idm.find()) {
|
||||
tokens.add(idm.group());
|
||||
tokenStarts.add(idm.start());
|
||||
tokenEnds.add(idm.end());
|
||||
}
|
||||
|
||||
int pkgSegments = result.packageSegmentCount;
|
||||
boolean hasPackage = pkgSegments > 0;
|
||||
|
||||
// Highlight package tokens (if any)
|
||||
if (result.found && result.invalidStartOffset < 0) {
|
||||
// Fully resolved - highlight package (blue) and class segments (typed)
|
||||
if (hasPackage) {
|
||||
int pkgEnd = pathStart + packagePortion.length();
|
||||
if (hasPackage && pkgSegments <= tokenEnds.size()) {
|
||||
int pkgEnd = pathStart + tokenEnds.get(pkgSegments - 1);
|
||||
marks.add(new Mark(pathStart, pkgEnd, TokenType.TYPE_DECL));
|
||||
}
|
||||
|
||||
if (hasClass && !result.classSegments.isEmpty()) {
|
||||
int classStart = pathStart + (hasPackage ? packagePortion.length() + 1 : 0);
|
||||
for (ClassPathFinder.ClassSegment seg : result.classSegments) {
|
||||
int segEnd = classStart + seg.name.length();
|
||||
|
||||
// Highlight class segments
|
||||
if (result.classSegments != null && !result.classSegments.isEmpty()) {
|
||||
int classTokenStart = hasPackage ? pkgSegments : 0;
|
||||
for (int si = 0; si < result.classSegments.size(); si++) {
|
||||
int tokIdx = classTokenStart + si;
|
||||
if (tokIdx >= 0 && tokIdx < tokenStarts.size()) {
|
||||
int s = pathStart + tokenStarts.get(tokIdx);
|
||||
int e = pathStart + tokenEnds.get(tokIdx);
|
||||
ClassPathFinder.ClassSegment seg = result.classSegments.get(si);
|
||||
TokenType segType;
|
||||
switch (seg.type) {
|
||||
case INTERFACE: segType = TokenType.INTERFACE_DECL; break;
|
||||
case ENUM: segType = TokenType.ENUM_DECL; break;
|
||||
default: segType = TokenType.IMPORTED_CLASS; break;
|
||||
}
|
||||
marks.add(new Mark(s, e, segType));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (result.classSegments != null && !result.classSegments.isEmpty()) {
|
||||
// Partial resolution - mark package and valid class segments
|
||||
if (hasPackage && pkgSegments <= tokenEnds.size()) {
|
||||
int pkgEnd = pathStart + tokenEnds.get(pkgSegments - 1);
|
||||
marks.add(new Mark(pathStart, pkgEnd, TokenType.TYPE_DECL));
|
||||
}
|
||||
|
||||
int classTokenStart = hasPackage ? pkgSegments : 0;
|
||||
for (int si = 0; si < result.classSegments.size(); si++) {
|
||||
int tokIdx = classTokenStart + si;
|
||||
if (tokIdx >= 0 && tokIdx < tokenStarts.size()) {
|
||||
int s = pathStart + tokenStarts.get(tokIdx);
|
||||
int e = pathStart + tokenEnds.get(tokIdx);
|
||||
ClassPathFinder.ClassSegment seg = result.classSegments.get(si);
|
||||
TokenType segType;
|
||||
switch (seg.type) {
|
||||
case INTERFACE: segType = TokenType.INTERFACE_DECL; break;
|
||||
case ENUM: segType = TokenType.ENUM_DECL; break;
|
||||
default: segType = TokenType.IMPORTED_CLASS; break;
|
||||
}
|
||||
marks.add(new Mark(classStart, segEnd, segType));
|
||||
classStart = segEnd + 1;
|
||||
marks.add(new Mark(s, e, segType));
|
||||
}
|
||||
}
|
||||
} else if (result.classSegments != null && !result.classSegments.isEmpty()) {
|
||||
// Partial resolution - some class segments are valid, rest is invalid
|
||||
// (e.g., IOverlay.ColorType resolved but ".idk" is invalid)
|
||||
if (hasPackage) {
|
||||
int pkgEnd = pathStart + packagePortion.length();
|
||||
marks.add(new Mark(pathStart, pkgEnd, TokenType.TYPE_DECL));
|
||||
}
|
||||
|
||||
int classStart = pathStart + (hasPackage ? packagePortion.length() + 1 : 0);
|
||||
for (ClassPathFinder.ClassSegment seg : result.classSegments) {
|
||||
int segEnd = classStart + seg.name.length();
|
||||
TokenType segType;
|
||||
switch (seg.type) {
|
||||
case INTERFACE: segType = TokenType.INTERFACE_DECL; break;
|
||||
case ENUM: segType = TokenType.ENUM_DECL; break;
|
||||
default: segType = TokenType.IMPORTED_CLASS; break;
|
||||
}
|
||||
marks.add(new Mark(classStart, segEnd, segType));
|
||||
classStart = segEnd + 1;
|
||||
}
|
||||
|
||||
// Mark remaining invalid portion in red
|
||||
|
||||
// Mark remaining invalid portion in red (map normalized offset to original)
|
||||
if (result.invalidStartOffset >= 0) {
|
||||
int invalidStart = pathStart + result.invalidStartOffset;
|
||||
if (invalidStart < pathEnd) {
|
||||
marks.add(new Mark(invalidStart, pathEnd, TokenType.UNDEFINED_VAR));
|
||||
int invalidAbs = mapNormalizedOffsetToOriginal(orig, result.invalidStartOffset, pathStart);
|
||||
if (invalidAbs < pathEnd) {
|
||||
marks.add(new Mark(invalidAbs, pathEnd, TokenType.UNDEFINED_VAR));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not resolved at all - use invalidStartOffset to determine valid/invalid split
|
||||
if (result.invalidStartOffset >= 0 && hasPackage) {
|
||||
// Valid package prefix, invalid class
|
||||
int pkgEnd = pathStart + packagePortion.length();
|
||||
marks.add(new Mark(pathStart, pkgEnd, TokenType.TYPE_DECL));
|
||||
|
||||
int invalidStart = pathStart + result.invalidStartOffset;
|
||||
if (invalidStart < pathEnd) {
|
||||
marks.add(new Mark(invalidStart, pathEnd, TokenType.UNDEFINED_VAR));
|
||||
if (pkgSegments <= tokenEnds.size()) {
|
||||
int pkgEnd = pathStart + tokenEnds.get(pkgSegments - 1);
|
||||
marks.add(new Mark(pathStart, pkgEnd, TokenType.TYPE_DECL));
|
||||
}
|
||||
} else if (fullPath.endsWith(".") && hasPackage && result.invalidStartOffset < 0 && !hasClass) {
|
||||
|
||||
int invalidAbs = mapNormalizedOffsetToOriginal(orig, result.invalidStartOffset, pathStart);
|
||||
if (invalidAbs < pathEnd) {
|
||||
marks.add(new Mark(invalidAbs, pathEnd, TokenType.UNDEFINED_VAR));
|
||||
}
|
||||
} else if (fullPath.endsWith(".") && hasPackage && result.invalidStartOffset < 0) {
|
||||
// Trailing dot on a fully-valid package - all blue
|
||||
marks.add(new Mark(pathStart, pathEnd, TokenType.TYPE_DECL));
|
||||
} else if (result.invalidStartOffset < 0 && hasPackage && !hasClass) {
|
||||
} else if (result.invalidStartOffset < 0 && hasPackage) {
|
||||
// All-lowercase path treated as valid package
|
||||
marks.add(new Mark(pathStart, pathEnd, TokenType.TYPE_DECL));
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user