feat(api): access node under caret/mouse and jump (PR #1903)
* Access node under caret/mouse and jump * Apply lint
This commit is contained in:
@@ -49,4 +49,19 @@ public interface JadxGuiContext {
|
||||
* Access to GUI settings
|
||||
*/
|
||||
JadxGuiSettings settings();
|
||||
|
||||
ICodeNodeRef getNodeUnderCaret();
|
||||
|
||||
ICodeNodeRef getNodeUnderMouse();
|
||||
|
||||
ICodeNodeRef getEnclosingNodeUnderCaret();
|
||||
|
||||
ICodeNodeRef getEnclosingNodeUnderMouse();
|
||||
|
||||
/**
|
||||
* Jump to a code ref
|
||||
*
|
||||
* @return if successfully jumped to the code ref
|
||||
*/
|
||||
boolean open(ICodeNodeRef ref);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.gui.plugins.context;
|
||||
|
||||
import java.awt.Container;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -14,7 +15,15 @@ import jadx.api.metadata.ICodeNodeRef;
|
||||
import jadx.api.plugins.gui.ISettingsGroup;
|
||||
import jadx.api.plugins.gui.JadxGuiContext;
|
||||
import jadx.api.plugins.gui.JadxGuiSettings;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.plugins.PluginContext;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
|
||||
import jadx.gui.ui.codearea.CodeArea;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class GuiPluginContext implements JadxGuiContext {
|
||||
@@ -86,4 +95,85 @@ public class GuiPluginContext implements JadxGuiContext {
|
||||
public @Nullable ISettingsGroup getCustomSettingsGroup() {
|
||||
return customSettingsGroup;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CodeArea getCodeArea() {
|
||||
Container contentPane = commonContext.getMainWindow().getTabbedPane().getSelectedContentPanel();
|
||||
if (contentPane instanceof AbstractCodeContentPanel) {
|
||||
AbstractCodeArea codeArea = ((AbstractCodeContentPanel) contentPane).getCodeArea();
|
||||
if (codeArea instanceof CodeArea) {
|
||||
return (CodeArea) codeArea;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICodeNodeRef getNodeUnderCaret() {
|
||||
CodeArea codeArea = getCodeArea();
|
||||
if (codeArea != null) {
|
||||
JNode nodeUnderCaret = codeArea.getNodeUnderCaret();
|
||||
if (nodeUnderCaret != null) {
|
||||
return nodeUnderCaret.getCodeNodeRef();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICodeNodeRef getNodeUnderMouse() {
|
||||
CodeArea codeArea = getCodeArea();
|
||||
if (codeArea != null) {
|
||||
JNode nodeUnderMouse = codeArea.getNodeUnderMouse();
|
||||
if (nodeUnderMouse != null) {
|
||||
return nodeUnderMouse.getCodeNodeRef();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICodeNodeRef getEnclosingNodeUnderCaret() {
|
||||
CodeArea codeArea = getCodeArea();
|
||||
if (codeArea != null) {
|
||||
JNode nodeUnderMouse = codeArea.getEnclosingNodeUnderCaret();
|
||||
if (nodeUnderMouse != null) {
|
||||
return nodeUnderMouse.getCodeNodeRef();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICodeNodeRef getEnclosingNodeUnderMouse() {
|
||||
CodeArea codeArea = getCodeArea();
|
||||
if (codeArea != null) {
|
||||
JNode nodeUnderMouse = codeArea.getEnclosingNodeUnderMouse();
|
||||
if (nodeUnderMouse != null) {
|
||||
return nodeUnderMouse.getCodeNodeRef();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(ICodeNodeRef ref) {
|
||||
JNodeCache cache = commonContext.getMainWindow().getWrapper().getCache().getNodeCache();
|
||||
JNode node;
|
||||
if (ref instanceof ClassNode) {
|
||||
node = cache.makeFrom(((ClassNode) ref).getJavaNode());
|
||||
} else if (ref instanceof MethodNode) {
|
||||
node = cache.makeFrom(((MethodNode) ref).getJavaNode());
|
||||
} else if (ref instanceof FieldNode) {
|
||||
node = cache.makeFrom(((FieldNode) ref).getJavaNode());
|
||||
} else {
|
||||
// Package node - cannot jump to it
|
||||
// TODO: Var node - might be possible
|
||||
return false;
|
||||
}
|
||||
|
||||
commonContext.getMainWindow().getTabbedPane().codeJump(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -213,6 +213,20 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
return getJNodeAtOffset(start);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JNode getEnclosingNodeUnderCaret() {
|
||||
int caretPos = getCaretPosition();
|
||||
Token token = modelToToken(caretPos);
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
int start = adjustOffsetForToken(token);
|
||||
if (start == -1) {
|
||||
start = caretPos;
|
||||
}
|
||||
return getEnclosingJNodeAtOffset(start);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JNode getNodeUnderMouse() {
|
||||
Point pos = UiUtils.getMousePosition(this);
|
||||
@@ -220,6 +234,22 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
return getJNodeAtOffset(offset);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JNode getEnclosingNodeUnderMouse() {
|
||||
Point pos = UiUtils.getMousePosition(this);
|
||||
int offset = adjustOffsetForToken(viewToToken(pos));
|
||||
return getEnclosingJNodeAtOffset(offset);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JNode getEnclosingJNodeAtOffset(int offset) {
|
||||
JavaNode javaNode = getEnclosingJavaNode(offset);
|
||||
if (javaNode != null) {
|
||||
return convertJavaNode(javaNode);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JNode getJNodeAtOffset(int offset) {
|
||||
JavaNode javaNode = getJavaNodeAtOffset(offset);
|
||||
@@ -253,6 +283,15 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
}
|
||||
}
|
||||
|
||||
public JavaNode getEnclosingJavaNode(int offset) {
|
||||
try {
|
||||
return getJadxWrapper().getDecompiler().getEnclosingNode(getCodeInfo(), offset);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Can't get java node by offset: {}", offset, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public JavaClass getJavaClassIfAtPos(int pos) {
|
||||
try {
|
||||
ICodeInfo codeInfo = getCodeInfo();
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import jadx.api.metadata.ICodeNodeRef
|
||||
import jadx.core.dex.nodes.MethodNode
|
||||
|
||||
val jadx = getJadxInstance()
|
||||
var savedBookmark: ICodeNodeRef? = null
|
||||
|
||||
jadx.gui.ifAvailable {
|
||||
addPopupMenuAction(
|
||||
"Set bookmark",
|
||||
enabled = { true },
|
||||
keyBinding = "B",
|
||||
action = ::setBookmark,
|
||||
)
|
||||
|
||||
addMenuAction(
|
||||
"Jump to bookmark",
|
||||
action = ::jumpToBookmark,
|
||||
)
|
||||
}
|
||||
|
||||
fun setBookmark(node: ICodeNodeRef) {
|
||||
val enclosing = jadx.gui.enclosingNodeUnderCaret ?: run {
|
||||
jadx.log.info { "No enclosing node" }
|
||||
return
|
||||
}
|
||||
|
||||
// You can bookmark a field, method or a class
|
||||
val target = if (enclosing is MethodNode) enclosing else node
|
||||
|
||||
jadx.log.info { "Setting bookmark to: $target" }
|
||||
savedBookmark = target
|
||||
}
|
||||
|
||||
fun jumpToBookmark() {
|
||||
if (savedBookmark == null) {
|
||||
jadx.log.info { "No bookmark" }
|
||||
} else {
|
||||
val res = jadx.gui.open(savedBookmark!!)
|
||||
if (!res) {
|
||||
jadx.log.info { "Failed to jump to bookmark" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import jadx.api.metadata.ICodeNodeRef
|
||||
|
||||
val jadx = getJadxInstance()
|
||||
|
||||
jadx.gui.ifAvailable {
|
||||
addPopupMenuAction(
|
||||
"Print enclosing symbols under caret or mouse",
|
||||
enabled = { true },
|
||||
keyBinding = "G",
|
||||
action = ::runAction,
|
||||
)
|
||||
}
|
||||
|
||||
fun runAction(node: ICodeNodeRef) {
|
||||
jadx.log.info { "Node under caret: ${jadx.gui.nodeUnderCaret}" }
|
||||
jadx.log.info { "Enclosing node under caret: ${jadx.gui.enclosingNodeUnderCaret}" }
|
||||
jadx.log.info { "Node under mouse: ${jadx.gui.nodeUnderMouse}" }
|
||||
jadx.log.info { "Enclosing Node under mouse: ${jadx.gui.enclosingNodeUnderMouse}" }
|
||||
}
|
||||
+11
@@ -40,6 +40,17 @@ class Gui(
|
||||
context().copyToClipboard(str)
|
||||
}
|
||||
|
||||
fun open(ref: ICodeNodeRef): Boolean = context().open(ref)
|
||||
|
||||
val nodeUnderCaret: ICodeNodeRef?
|
||||
get() = context().nodeUnderCaret
|
||||
val nodeUnderMouse: ICodeNodeRef?
|
||||
get() = context().nodeUnderMouse
|
||||
val enclosingNodeUnderCaret: ICodeNodeRef?
|
||||
get() = context().enclosingNodeUnderCaret
|
||||
val enclosingNodeUnderMouse: ICodeNodeRef?
|
||||
get() = context().enclosingNodeUnderMouse
|
||||
|
||||
private fun context(): JadxGuiContext =
|
||||
guiContext ?: throw IllegalStateException("GUI plugins context not available!")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user