mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 22:27:32 +00:00
Heavily improved global/local/ this.field syntax highlight detection
This commit is contained in:
1 parent
91a6a166bb
commit
b3a64f92d7
2 files changed
+97
-4
No files matched your search
@@ -95,12 +95,20 @@ public class JavaTextContainer extends TextContainer {
|
||||
|
||||
// Extract method blocks first
|
||||
methodBlocks = MethodBlock.collectMethodBlocks(text);
|
||||
|
||||
// Get excluded ranges (strings and comments) for the entire text
|
||||
List<int[]> excludedRanges = MethodBlock.getExcludedRanges(text);
|
||||
|
||||
// Global fields (excluding those inside methods)
|
||||
// Global fields (excluding those inside methods, strings, and comments)
|
||||
Matcher mGlobal = GLOBAL_FIELD_DECL.matcher(text);
|
||||
while (mGlobal.find()) {
|
||||
String varName = mGlobal.group(2);
|
||||
int varPosition = mGlobal.start(2);
|
||||
|
||||
// Skip if inside a string or comment
|
||||
if (isInExcludedRange(varPosition, excludedRanges)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this variable is inside a method
|
||||
boolean isInsideMethod = false;
|
||||
@@ -157,6 +165,35 @@ public class JavaTextContainer extends TextContainer {
|
||||
"Boolean", "Long", "Byte", "Short", "Character", "List", "Map", "Set"
|
||||
));
|
||||
|
||||
// First pass: handle field accesses (this.field and obj.field patterns)
|
||||
Pattern thisFieldPattern = Pattern.compile("\\bthis\\s*\\.\\s*([a-zA-Z_][a-zA-Z0-9_]*)");
|
||||
Matcher thisFieldMatcher = thisFieldPattern.matcher(text);
|
||||
while (thisFieldMatcher.find()) {
|
||||
String fieldName = thisFieldMatcher.group(1);
|
||||
int fieldPos = thisFieldMatcher.start(1);
|
||||
// Highlight as GLOBAL_FIELD if it exists, UNDEFINED_VAR otherwise
|
||||
if (globalFields.contains(fieldName)) {
|
||||
marks.add(new Mark(fieldPos, thisFieldMatcher.end(1), TokenType.GLOBAL_FIELD));
|
||||
} else {
|
||||
marks.add(new Mark(fieldPos, thisFieldMatcher.end(1), TokenType.UNDEFINED_VAR));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle identifier.field patterns (e.g., obj.field, global.field)
|
||||
// The field part should be highlighted as GLOBAL_FIELD (light blue)
|
||||
Pattern objFieldPattern = Pattern.compile("\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\.\\s*([a-zA-Z_][a-zA-Z0-9_]*)");
|
||||
Matcher objFieldMatcher = objFieldPattern.matcher(text);
|
||||
while (objFieldMatcher.find()) {
|
||||
String fieldName = objFieldMatcher.group(2);
|
||||
int fieldPos = objFieldMatcher.start(2);
|
||||
// Skip if this is a "this.field" pattern (already handled above)
|
||||
String objName = objFieldMatcher.group(1);
|
||||
if (!objName.equals("this")) {
|
||||
// Highlight the field part as GLOBAL_FIELD (represents another object's field)
|
||||
marks.add(new Mark(fieldPos, objFieldMatcher.end(2), TokenType.GLOBAL_FIELD));
|
||||
}
|
||||
}
|
||||
|
||||
Pattern identifier = Pattern.compile("\\b([a-zA-Z_][a-zA-Z0-9_]*)\\b");
|
||||
Matcher m = identifier.matcher(text);
|
||||
while (m.find()) {
|
||||
@@ -177,10 +214,11 @@ public class JavaTextContainer extends TextContainer {
|
||||
MethodBlock methodBlock = findMethodBlockAtPosition(position);
|
||||
|
||||
if (methodBlock != null) {
|
||||
// Inside a method - check in order: parameter, local, global
|
||||
// Inside a method - check in order: parameter, local (position-aware), global
|
||||
if (methodBlock.parameters.contains(name)) {
|
||||
marks.add(new Mark(m.start(1), m.end(1), TokenType.PARAMETER));
|
||||
} else if (methodBlock.localVariables.contains(name)) {
|
||||
} else if (methodBlock.isLocalDeclaredAtPosition(name, position)) {
|
||||
// Local variable is declared at or before this position
|
||||
marks.add(new Mark(m.start(1), m.end(1), TokenType.LOCAL_FIELD));
|
||||
} else if (globalFields.contains(name)) {
|
||||
marks.add(new Mark(m.start(1), m.end(1), TokenType.GLOBAL_FIELD));
|
||||
@@ -249,6 +287,18 @@ public class JavaTextContainer extends TextContainer {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if position is in an excluded range (string or comment)
|
||||
*/
|
||||
private boolean isInExcludedRange(int pos, List<int[]> ranges) {
|
||||
for (int[] range : ranges) {
|
||||
if (pos >= range[0] && pos < range[1]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void formatCodeText() {
|
||||
// Step 1: Tokenize the full text
|
||||
List<Mark> marks = new ArrayList<>();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package noppes.npcs.client.gui.util.script;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -26,6 +28,7 @@ public class MethodBlock {
|
||||
public String text;
|
||||
public List<String> localVariables = new ArrayList<>();
|
||||
public List<String> parameters = new ArrayList<>(); // Method parameters
|
||||
public Map<String, Integer> localVarPositions = new HashMap<>(); // Track declaration positions
|
||||
|
||||
public MethodBlock(int start, int end, String text) {
|
||||
this.startOffset = start;
|
||||
@@ -63,14 +66,54 @@ public class MethodBlock {
|
||||
// Extract local variables declared within this method
|
||||
private void extractLocalVariables() {
|
||||
localVariables.clear();
|
||||
localVarPositions.clear();
|
||||
|
||||
// Get excluded ranges (strings and comments) for this method's text
|
||||
List<int[]> excludedRanges = getExcludedRanges(text);
|
||||
|
||||
Matcher m = LOCAL_VAR_DECL.matcher(text);
|
||||
while (m.find()) {
|
||||
String varName = m.group(2);
|
||||
if (!localVariables.contains(varName)) {
|
||||
int declPosition = m.start();
|
||||
|
||||
// Skip if inside a string or comment
|
||||
if (isInExcludedRange(declPosition, excludedRanges)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this is a "this.field" assignment - if so, skip it
|
||||
// Look backwards from the declaration position to see if preceded by "this."
|
||||
boolean isThisFieldAssignment = false;
|
||||
if (declPosition >= 5) {
|
||||
int checkStart = Math.max(0, declPosition - 10);
|
||||
String before = text.substring(checkStart, declPosition);
|
||||
if (before.matches(".*\\bthis\\s*\\.\\s*$")) {
|
||||
isThisFieldAssignment = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isThisFieldAssignment && !localVariables.contains(varName)) {
|
||||
localVariables.add(varName);
|
||||
// Store the absolute position in the full text (startOffset + relative position)
|
||||
localVarPositions.put(varName, startOffset + declPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a reference position comes before the local variable declaration
|
||||
// This handles cases like: global.field on line 7, then var global = "x" on line 9
|
||||
// At line 7, the local 'global' doesn't exist yet, so it should refer to the global 'global'
|
||||
public boolean isLocalDeclaredAtPosition(String varName, int absolutePosition) {
|
||||
if (!localVariables.contains(varName)) {
|
||||
return false;
|
||||
}
|
||||
Integer declPos = localVarPositions.get(varName);
|
||||
if (declPos == null) {
|
||||
return false;
|
||||
}
|
||||
// The local variable is only "in scope" at or after its declaration position
|
||||
return absolutePosition >= declPos;
|
||||
}
|
||||
|
||||
// Check if a position (in the full text) falls within this method
|
||||
public boolean containsPosition(int position) {
|
||||
|
||||
Reference in new issue
Block a user