diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java index 6cf813175a6..8da27240d01 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java @@ -272,40 +272,68 @@ public String getWordUnderCaret() { 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 void centerCurrentLine() { } /** - * @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); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java index 86bfb24cc0f..a58e00b0e83 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java @@ -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 @@ private boolean jumpOnDoubleClick(MouseEvent e) { 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 @@ private void addMenuForJsonFile() { 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 @@ private JNode convertJavaNode(JavaNode javaNode) { @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 JNode getEnclosingNodeUnderCaret() { @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 JavaNode getJavaNodeAtOffset(int offset) { } 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 JavaNode getClosestJavaNode(int offset) { } public JavaNode getEnclosingJavaNode(int offset) { + if (offset == -1) { + return null; + } try { return getJadxWrapper().getDecompiler().getEnclosingNode(getCodeInfo(), offset); } catch (Exception e) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeLinkGenerator.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeLinkGenerator.java index 87b6775a733..3525c21d1a6 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeLinkGenerator.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeLinkGenerator.java @@ -7,7 +7,6 @@ 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 JavaNode getNodeAtOffset(int offset) { 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 LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) 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 int getSourceOffset() { } } - 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);