fix(gui): highlight word even if cursor at the end of that word (#2083)

This commit is contained in:
Skylot
2024-01-18 19:40:53 +00:00
parent fb703cd856
commit 4483533417
3 changed files with 66 additions and 55 deletions
@@ -272,40 +272,68 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
return getWordByPosition(getCaretPosition());
}
@Nullable
public String getWordByPosition(int pos) {
public @Nullable String getWordByPosition(int offset) {
Token token = getWordTokenAtOffset(offset);
if (token != null) {
return token.getLexeme();
}
return null;
}
/**
* Return any word token (not whitespace or special symbol) at offset.
* Select the previous token if the cursor at word end (current token already is whitespace)
*/
public @Nullable Token getWordTokenAtOffset(int offset) {
try {
Token token = modelToToken(pos);
return getWordFromToken(token);
int line = this.getLineOfOffset(offset);
Token lineTokens = this.getTokenListForLine(line);
Token token = null;
Token prevToken = null;
for (Token t = lineTokens; t != null && t.isPaintable(); t = t.getNextToken()) {
if (t.containsPosition(offset)) {
token = t;
break;
}
prevToken = t;
}
if (token == null) {
return null;
}
if (isWordToken(token)) {
return token;
}
if (isWordToken(prevToken)) {
return prevToken;
}
return null;
} catch (Exception e) {
LOG.error("Failed to get word at pos: {}", pos, e);
LOG.error("Failed to get token at pos: {}", offset, e);
return null;
}
}
@Nullable
private static String getWordFromToken(@Nullable Token token) {
public static boolean isWordToken(@Nullable Token token) {
if (token == null) {
return null;
return false;
}
switch (token.getType()) {
case TokenTypes.NULL:
case TokenTypes.WHITESPACE:
case TokenTypes.SEPARATOR:
case TokenTypes.OPERATOR:
return null;
case TokenTypes.FUNCTION:
return false;
case TokenTypes.IDENTIFIER:
if (token.length() == 1) {
char ch = token.charAt(0);
if (ch == ';' || ch == '.') {
return null;
}
return ch != ';' && ch != '.' && ch != ',';
}
return token.getLexeme();
return true;
default:
return token.getLexeme();
return true;
}
}
@@ -386,8 +414,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
}
/**
* @param str
* - if null -> reset current highlights
* @param str - if null -> reset current highlights
*/
private void highlightAllMatches(@Nullable String str) {
SearchContext context = new SearchContext(str);
@@ -68,7 +68,7 @@ public final class CodeArea extends AbstractCodeArea {
@Override
public void mouseClicked(MouseEvent e) {
if (e.isControlDown() || jumpOnDoubleClick(e)) {
navToDecl(e.getPoint(), codeLinkGenerator);
navToDecl(e.getPoint());
}
}
});
@@ -82,10 +82,9 @@ public final class CodeArea extends AbstractCodeArea {
return e.getClickCount() == 2 && getMainWindow().getSettings().isJumpOnDoubleClick();
}
@SuppressWarnings("deprecation")
private void navToDecl(Point point, CodeLinkGenerator codeLinkGenerator) {
int offs = viewToModel(point);
JNode node = getJNodeAtOffset(codeLinkGenerator.getLinkSourceOffset(offs));
private void navToDecl(Point point) {
int offs = viewToModel2D(point);
JNode node = getJNodeAtOffset(adjustOffsetForWordToken(offs));
if (node != null) {
contentPanel.getTabbedPane().codeJump(node);
}
@@ -154,17 +153,16 @@ public final class CodeArea extends AbstractCodeArea {
popup.add(new JsonPrettifyAction(this));
}
public int adjustOffsetForToken(@Nullable Token token) {
/**
* Search start of word token at specified offset
*
* @return -1 if no word token found
*/
public int adjustOffsetForWordToken(int offset) {
Token token = getWordTokenAtOffset(offset);
if (token == null) {
return -1;
}
// fast skip
if (token.length() == 1) {
char ch = token.getTextArray()[token.getTextOffset()];
if (ch == '.' || ch == ',' || ch == ';') {
return -1;
}
}
int type = token.getType();
if (node instanceof JClass) {
if (type == TokenTypes.IDENTIFIER || type == TokenTypes.FUNCTION) {
@@ -208,25 +206,13 @@ public final class CodeArea extends AbstractCodeArea {
@Nullable
public JNode getNodeUnderCaret() {
int caretPos = getCaretPosition();
Token token = modelToToken(caretPos);
if (token == null) {
return null;
}
int start = adjustOffsetForToken(token);
if (start == -1) {
start = caretPos;
}
return getJNodeAtOffset(start);
return getJNodeAtOffset(adjustOffsetForWordToken(caretPos));
}
@Nullable
public JNode getEnclosingNodeUnderCaret() {
int caretPos = getCaretPosition();
Token token = modelToToken(caretPos);
if (token == null) {
return null;
}
int start = adjustOffsetForToken(token);
int start = adjustOffsetForWordToken(caretPos);
if (start == -1) {
start = caretPos;
}
@@ -236,15 +222,13 @@ public final class CodeArea extends AbstractCodeArea {
@Nullable
public JNode getNodeUnderMouse() {
Point pos = UiUtils.getMousePosition(this);
int offset = adjustOffsetForToken(viewToToken(pos));
return getJNodeAtOffset(offset);
return getJNodeAtOffset(adjustOffsetForWordToken(viewToModel2D(pos)));
}
@Nullable
public JNode getEnclosingNodeUnderMouse() {
Point pos = UiUtils.getMousePosition(this);
int offset = adjustOffsetForToken(viewToToken(pos));
return getEnclosingJNodeAtOffset(offset);
return getEnclosingJNodeAtOffset(adjustOffsetForWordToken(viewToModel2D(pos)));
}
@Nullable
@@ -281,6 +265,9 @@ public final class CodeArea extends AbstractCodeArea {
}
public JavaNode getClosestJavaNode(int offset) {
if (offset == -1) {
return null;
}
try {
return getJadxWrapper().getDecompiler().getClosestJavaNode(getCodeInfo(), offset);
} catch (Exception e) {
@@ -290,6 +277,9 @@ public final class CodeArea extends AbstractCodeArea {
}
public JavaNode getEnclosingJavaNode(int offset) {
if (offset == -1) {
return null;
}
try {
return getJadxWrapper().getDecompiler().getEnclosingNode(getCodeInfo(), offset);
} catch (Exception e) {
@@ -7,7 +7,6 @@ import javax.swing.event.HyperlinkEvent;
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,7 +31,7 @@ public class CodeLinkGenerator implements LinkGenerator {
if (!codeArea.getCodeInfo().hasMetadata()) {
return null;
}
int sourceOffset = getLinkSourceOffset(offset);
int sourceOffset = codeArea.adjustOffsetForWordToken(offset);
if (sourceOffset == -1) {
return null;
}
@@ -49,7 +48,7 @@ public class CodeLinkGenerator implements LinkGenerator {
if (!codeArea.getCodeInfo().hasMetadata()) {
return null;
}
int sourceOffset = getLinkSourceOffset(offset);
int sourceOffset = codeArea.adjustOffsetForWordToken(offset);
if (sourceOffset == -1) {
return null;
}
@@ -75,11 +74,6 @@ public class CodeLinkGenerator implements LinkGenerator {
}
}
public int getLinkSourceOffset(int offset) {
Token token = codeArea.modelToToken(offset);
return codeArea.adjustOffsetForToken(token);
}
@Nullable
private JumpPosition getJumpBySourceOffset(int sourceOffset) {
final JumpPosition defPos = codeArea.getDefPosForNodeAtOffset(sourceOffset);