未验证 提交 ffb2956d 编写于 作者: L LBJ-the-GOAT 提交者: GitHub

fix(gui): fix caret positions of search/usage/goto decl, add search to popup...

fix(gui): fix caret positions of search/usage/goto decl, add search to popup menu (#1093) (PR #1094)

* fix caret positions of search/usage/goto decl to matched place & add menu items for search

* Remove static field for main window

Co-authored-by: tobias <tobias.hotmail.com>
上级 9744547f
......@@ -5,6 +5,7 @@ public final class CodePosition {
private final JavaNode node;
private final int line;
private final int offset;
private int usagePosition = -1;
public CodePosition(JavaNode node, int line, int offset) {
this.node = node;
......@@ -18,6 +19,15 @@ public final class CodePosition {
this.offset = offset;
}
public int getUsagePosition() {
return usagePosition;
}
public CodePosition setUsagePosition(int usagePosition) {
this.usagePosition = usagePosition;
return this;
}
public JavaNode getNode() {
return node;
}
......
......@@ -120,8 +120,16 @@ public class CodeWriter {
CodeWriter add(CodeWriter code) {
line--;
for (Map.Entry<CodePosition, Object> entry : code.annotations.entrySet()) {
Object val = entry.getValue();
if (val instanceof DefinitionWrapper) {
LineAttrNode node = ((DefinitionWrapper) val).getNode();
node.setDefPosition(node.getDefPosition() + this.buf.length());
}
CodePosition pos = entry.getKey();
attachAnnotation(entry.getValue(), new CodePosition(line + pos.getLine(), pos.getOffset()));
int usagePos = pos.getUsagePosition() + bufLength();
attachAnnotation(val,
new CodePosition(line + pos.getLine(), pos.getOffset())
.setUsagePosition(usagePos));
}
for (Map.Entry<Integer, Integer> entry : code.lineMap.entrySet()) {
attachSourceLine(line + entry.getKey(), entry.getValue());
......@@ -211,12 +219,14 @@ public class CodeWriter {
}
public void attachDefinition(LineAttrNode obj) {
obj.setDefPosition(buf.length());
attachAnnotation(obj);
attachAnnotation(new DefinitionWrapper(obj), new CodePosition(line, offset));
}
public void attachAnnotation(Object obj) {
attachAnnotation(obj, new CodePosition(line, offset + 1));
attachAnnotation(obj,
new CodePosition(line, offset + 1).setUsagePosition(bufLength()));
}
public void attachLineAnnotation(Object obj) {
......
......@@ -8,6 +8,17 @@ public abstract class LineAttrNode extends AttrNode {
private int decompiledLine;
// the position exactly where a node declared at in decompiled java code.
private int defPosition;
public int getDefPosition() {
return this.defPosition;
}
public void setDefPosition(int defPosition) {
this.defPosition = defPosition;
}
public int getSourceLine() {
return sourceLine;
}
......
......@@ -13,6 +13,8 @@ public class CodeNode extends JNode {
private final transient JClass jParent;
private final transient StringRef line;
private final transient int lineNum;
private transient int pos = -1;
private transient boolean precise;
public CodeNode(JNode jNode, int lineNum, StringRef lineStr) {
this.jNode = jNode;
......@@ -93,4 +95,25 @@ public class CodeNode extends JNode {
public int hashCode() {
return jNode.hashCode();
}
public int getPos() {
return pos;
}
public CodeNode setPos(int pos) {
this.pos = pos;
return this;
}
public CodeNode setPrecisePos(int pos) {
this.pos = pos;
if (pos > -1) {
this.precise = true;
}
return this;
}
public boolean isPrecisePos() {
return precise;
}
}
......@@ -29,6 +29,10 @@ public class JField extends JNode {
this.jParent = jClass;
}
public JavaField getJavaField() {
return (JavaField) getJavaNode();
}
@Override
public JavaNode getJavaNode() {
return field;
......
......@@ -11,7 +11,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.*;
......@@ -20,7 +19,6 @@ import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.fife.ui.rsyntaxtextarea.DocumentRange;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rtextarea.SearchContext;
......@@ -33,7 +31,7 @@ import org.slf4j.LoggerFactory;
import jadx.gui.jobs.BackgroundJob;
import jadx.gui.jobs.BackgroundWorker;
import jadx.gui.jobs.DecompileJob;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.*;
import jadx.gui.ui.codearea.AbstractCodeArea;
import jadx.gui.utils.CacheObject;
import jadx.gui.utils.JumpPosition;
......@@ -111,9 +109,18 @@ public abstract class CommonSearchDialog extends JDialog {
if (selectedId == -1) {
return;
}
int pos = Math.max(0, resultsModel.renderer.getFirstMarkOfCode(selectedId));
JumpPosition jmpPos;
JNode node = (JNode) resultsModel.getValueAt(selectedId, 0);
tabbedPane.codeJump(new JumpPosition(node.getRootClass(), node.getLine(), pos));
if (node instanceof CodeNode) {
CodeNode codeNode = (CodeNode) node;
jmpPos = new JumpPosition(node.getRootClass(), node.getLine(), codeNode.getPos());
if (codeNode.isPrecisePos()) {
jmpPos.setPrecise(codeNode.getPos());
}
} else {
jmpPos = new JumpPosition(node.getRootClass(), node.getLine());
}
tabbedPane.codeJump(jmpPos);
dispose();
}
......@@ -413,19 +420,6 @@ public abstract class CommonSearchDialog extends JDialog {
this.codeBackground = area.getBackground();
}
public int getFirstMarkOfCode(int row) {
Component comp = componentCache.get(makeID(row, 1));
if (comp instanceof RSyntaxTextArea) {
List<DocumentRange> ranges = ((RSyntaxTextArea) comp).getMarkAllHighlightRanges();
if (ranges.size() > 0) {
// minus 2 cuz the start of textArea of the column is added 2
// spaces in makeCell method.
return ranges.get(0).getStartOffset() - 2;
}
}
return 0;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object obj, boolean isSelected,
boolean hasFocus, int row, int column) {
......
......@@ -81,6 +81,7 @@ import jadx.api.JadxArgs;
import jadx.api.JavaClass;
import jadx.api.JavaNode;
import jadx.api.ResourceFile;
import jadx.core.utils.StringUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.files.FileUtils;
import jadx.gui.JadxWrapper;
......@@ -100,6 +101,7 @@ import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JPackage;
import jadx.gui.treemodel.JResource;
import jadx.gui.treemodel.JRoot;
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
import jadx.gui.update.JadxUpdate;
import jadx.gui.update.JadxUpdate.IUpdateCallback;
import jadx.gui.update.data.Release;
......@@ -228,7 +230,8 @@ public class MainWindow extends JFrame {
return;
}
JNode node = cacheObject.getNodeCache().makeFrom(javaNode);
tabbedPane.codeJump(new JumpPosition(node.getRootClass(), node.getLine()));
tabbedPane.codeJump(new JumpPosition(node.getRootClass(), node.getLine())
.setPrecise(JumpPosition.getDefPos(node)));
}
}
......@@ -513,7 +516,8 @@ public class MainWindow extends JFrame {
continue;
}
JNode newNode = cacheObject.getNodeCache().makeFrom(newClass);
tabbedPane.codeJump(new JumpPosition(newNode, position));
tabbedPane.codeJump(new JumpPosition(newNode, position)
.setPrecise(JumpPosition.getDefPos(newNode)));
}
}
......@@ -646,7 +650,8 @@ public class MainWindow extends JFrame {
JNode node = (JNode) obj;
JClass cls = node.getRootClass();
if (cls != null) {
tabbedPane.codeJump(new JumpPosition(cls, node.getLine()));
tabbedPane.codeJump(new JumpPosition(cls, node.getLine())
.setPrecise(JumpPosition.getDefPos(node)));
}
}
} catch (Exception e) {
......@@ -812,6 +817,14 @@ public class MainWindow extends JFrame {
Action textSearchAction = new AbstractAction(NLS.str("menu.text_search"), ICON_SEARCH) {
@Override
public void actionPerformed(ActionEvent e) {
ContentPanel panel = tabbedPane.getSelectedCodePanel();
if (panel instanceof AbstractCodeContentPanel) {
String preferText = ((AbstractCodeContentPanel) panel).getCodeArea().getSelectedText();
if (!StringUtils.isEmpty(preferText)) {
SearchDialog.searchText(MainWindow.this, preferText);
return;
}
}
new SearchDialog(MainWindow.this, true).setVisible(true);
}
};
......
......@@ -21,6 +21,7 @@ import io.reactivex.Flowable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import jadx.core.utils.StringUtils;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.NLS;
import jadx.gui.utils.TextStandardActions;
......@@ -47,6 +48,7 @@ public class SearchDialog extends CommonSearchDialog {
private transient Disposable searchDisposable;
private transient SearchEventEmitter searchEmitter;
private transient String text = null;
public SearchDialog(MainWindow mainWindow, boolean textSearch) {
super(mainWindow);
......@@ -286,13 +288,23 @@ public class SearchDialog extends CommonSearchDialog {
@Override
protected void loadFinished() {
if (!StringUtils.isEmpty(text)) {
searchField.setText(text);
}
resultsTable.setEnabled(true);
searchField.setEnabled(true);
}
@Override
protected void loadStart() {
text = cache.getLastSearch(); // SearchDialog is opened by menu item, let loadFinished to set text
cache.setLastSearch("");
resultsTable.setEnabled(false);
searchField.setEnabled(false);
}
public static void searchText(MainWindow window, String text) {
window.getCacheObject().setLastSearch(text);
new SearchDialog(window, true).setVisible(true);
}
}
......@@ -168,7 +168,7 @@ public class TabbedPane extends JTabbedPane {
line = pos.getNode().getLine();
}
}
if (pos.getPos() <= 0) {
if (pos.getPos() < 0) {
codeArea.scrollToLine(line);
} else {
int lineNum = Math.max(0, line - 1);
......@@ -225,6 +225,9 @@ public class TabbedPane extends JTabbedPane {
}
public void navBack() {
if (jumps.size() > 1) {
jumps.updateCurPosition(getCurrentPosition());
}
JumpPosition pos = jumps.getPrev();
if (pos != null) {
showCode(pos);
......@@ -232,6 +235,9 @@ public class TabbedPane extends JTabbedPane {
}
public void navForward() {
if (jumps.size() > 1) {
jumps.updateCurPosition(getCurrentPosition());
}
JumpPosition pos = jumps.getNext();
if (pos != null) {
showCode(pos);
......
......@@ -32,11 +32,6 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
public static final Color MARK_ALL_HIGHLIGHT_COLOR = Color.decode("#FFED89");
@Override
public boolean getHighlightCurrentLine() {
return super.getHighlightCurrentLine();
}
protected final ContentPanel contentPanel;
protected final JNode node;
......@@ -115,13 +110,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
@Override
public void caretUpdate(CaretEvent e) {
int pos = e.getDot();
if (pos == 0) {
// not accepting 0, cuz sometimes the underlying RSyntaxTextArea
// will fire a fake event to force repaint caret, and its dot is
// usually 0, so we just ignore 0 anyway as a workaround.
return;
}
int pos = getCaretPosition();
if (lastPos != pos) {
lastPos = pos;
lastText = highlightCaretWord(lastText, pos);
......
......@@ -145,7 +145,8 @@ public final class CodeArea extends AbstractCodeArea {
return null;
}
JNode jNode = convertJavaNode(foundNode);
return new JumpPosition(jNode.getRootClass(), pos.getLine());
return new JumpPosition(jNode.getRootClass(), pos.getLine())
.setPrecise(JumpPosition.getDefPos(jNode));
}
private JNode convertJavaNode(JavaNode javaNode) {
......
......@@ -8,9 +8,16 @@ import java.util.Map;
import java.util.Set;
import javax.swing.*;
import javax.swing.JPopupMenu.Separator;
import javax.swing.border.EmptyBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import jadx.api.ICodeInfo;
import jadx.core.utils.StringUtils;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.SearchDialog;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
/**
......@@ -42,6 +49,56 @@ public class CodePanel extends JPanel {
searchBar.toggle();
}
});
JMenuItem searchItem = new JMenuItem();
JMenuItem globalSearchItem = new JMenuItem();
AbstractAction searchAction = new AbstractAction(NLS.str("popup.search", "")) {
@Override
public void actionPerformed(ActionEvent e) {
searchBar.toggle();
}
};
AbstractAction globalSearchAction = new AbstractAction(NLS.str("popup.search_global", "")) {
@Override
public void actionPerformed(ActionEvent e) {
MainWindow mainWindow = codeArea.getContentPanel().getTabbedPane().getMainWindow();
SearchDialog.searchText(mainWindow, codeArea.getSelectedText());
}
};
searchItem.setAction(searchAction);
globalSearchItem.setAction(globalSearchAction);
Separator separator = new Separator();
JPopupMenu popupMenu = codeArea.getPopupMenu();
popupMenu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
String preferText = codeArea.getSelectedText();
if (!StringUtils.isEmpty(preferText)) {
if (preferText.length() >= 23) {
preferText = preferText.substring(0, 20) + " ...";
}
searchAction.putValue(Action.NAME, NLS.str("popup.search", preferText));
globalSearchAction.putValue(Action.NAME, NLS.str("popup.search_global", preferText));
popupMenu.add(separator);
popupMenu.add(globalSearchItem);
popupMenu.add(searchItem);
} else {
popupMenu.remove(separator);
popupMenu.remove(globalSearchItem);
popupMenu.remove(searchItem);
}
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
}
public void loadSettings() {
......
......@@ -15,6 +15,7 @@ import org.fife.ui.rtextarea.SearchResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.StringUtils;
import jadx.gui.utils.NLS;
import jadx.gui.utils.TextStandardActions;
import jadx.gui.utils.UiUtils;
......@@ -113,6 +114,10 @@ class SearchBar extends JToolBar {
setVisible(visible);
if (visible) {
String preferText = rTextArea.getSelectedText();
if (!StringUtils.isEmpty(preferText)) {
searchField.setText(preferText);
}
searchField.requestFocus();
searchField.selectAll();
} else {
......
......@@ -64,7 +64,7 @@ public class CodeUsageInfo {
JavaNode javaNodeByLine = linesInfo.getJavaNodeByLine(line);
StringRef codeLine = lines.get(line - 1);
JNode node = nodeCache.makeFrom(javaNodeByLine == null ? javaClass : javaNodeByLine);
CodeNode codeNode = new CodeNode(node, line, codeLine);
CodeNode codeNode = new CodeNode(node, line, codeLine).setPrecisePos(codePosition.getUsagePosition());
usageInfo.addUsage(codeNode);
}
......
......@@ -28,6 +28,14 @@ public class JumpManager {
}
}
public void updateCurPosition(JumpPosition pos) {
list.set(currentPos, pos);
}
public int size() {
return list.size();
}
private boolean ignoreJump(JumpPosition pos) {
JumpPosition current = getCurrent();
if (current == null) {
......
package jadx.gui.utils;
import jadx.gui.treemodel.JNode;
import jadx.api.JavaClass;
import jadx.api.JavaField;
import jadx.api.JavaMethod;
import jadx.api.JavaNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.treemodel.*;
public class JumpPosition {
private final JNode node;
private final int line;
// the position of the node in java code,
// call codeArea.scrollToPos(pos) to set caret
private int pos;
// Precise means caret can be set right at the node in codeArea,
// not just the start of the line.
private boolean precise;
public JumpPosition(JNode node, int line) {
this(node, line, 0);
this(node, line, -1);
}
public JumpPosition(JNode node, int line, int pos) {
......@@ -44,6 +45,32 @@ public class JumpPosition {
return line;
}
public static int getDefPos(JNode node) {
if (node instanceof JClass) {
return ((JClass) node).getCls().getClassNode().getDefPosition();
}
if (node instanceof JMethod) {
return ((JMethod) node).getJavaMethod().getMethodNode().getDefPosition();
}
if (node instanceof JField) {
return ((JField) node).getJavaField().getFieldNode().getDefPosition();
}
throw new JadxRuntimeException("Unexpected node " + node);
}
public static int getDefPos(JavaNode node) {
if (node instanceof JavaClass) {
return ((JavaClass) node).getClassNode().getDefPosition();
}
if (node instanceof JavaMethod) {
return ((JavaMethod) node).getMethodNode().getDefPosition();
}
if (node instanceof JavaField) {
return ((JavaField) node).getFieldNode().getDefPosition();
}
throw new JadxRuntimeException("Unexpected node " + node);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
......
......@@ -35,7 +35,9 @@ public class CodeIndex {
return Flowable.create(emitter -> {
LOG.debug("Code search started: {} ...", searchSettings.getSearchString());
for (CodeNode node : values) {
if (isMatched(node.getLineStr(), searchSettings)) {
int pos = searchSettings.find(node.getLineStr());
node.setPos(pos);
if (pos > -1) {
emitter.onNext(node);
}
if (emitter.isCancelled()) {
......
......@@ -158,7 +158,7 @@ public class TextSearchIndex {
int lineStart = 1 + code.lastIndexOf(CodeWriter.NL, pos);
int lineEnd = code.indexOf(CodeWriter.NL, pos + searchSettings.getSearchString().length());
StringRef line = StringRef.subString(code, lineStart, lineEnd == -1 ? code.length() : lineEnd);
emitter.onNext(new CodeNode(nodeCache.makeFrom(javaClass), -pos, line.trim()));
emitter.onNext(new CodeNode(nodeCache.makeFrom(javaClass), -pos, line.trim()).setPos(pos));
return lineEnd;
}
......
......@@ -160,6 +160,8 @@ popup.find_usage=Verwendung suchen
popup.go_to_declaration=Zur Erklärung gehen
popup.exclude=Ausschließen
popup.rename=Umbennen
#popup.search=
#popup.search_global=
confirm.save_as_title=Speichern unter bestätigen
confirm.save_as_message=%s existiert bereits.\nErsetzen?
......
......@@ -160,6 +160,8 @@ popup.find_usage=Find Usage
popup.go_to_declaration=Go to declaration
popup.exclude=Exclude
popup.rename=Rename
popup.search=Search "%s"
popup.search_global=Global Search "%s"
confirm.save_as_title=Confirm Save as
confirm.save_as_message=%s already exists.\nDo you want to replace it?
......
......@@ -160,6 +160,8 @@ popup.select_all=Seleccionar todo
#popup.go_to_declaration=
#popup.exclude=
popup.rename=Nimeta ümber
#popup.search=
#popup.search_global=
#confirm.save_as_title=
#confirm.save_as_message=
......
......@@ -160,6 +160,8 @@ popup.find_usage=사용 찾기
popup.go_to_declaration=선언문으로 이동
popup.exclude=제외
popup.rename=이름 바꾸기
#popup.search=
#popup.search_global=
confirm.save_as_title=다른 이름으로 저장 확인
confirm.save_as_message=%s이(가) 이미 있습니다.\n바꾸시겠습니까?
......
......@@ -160,6 +160,8 @@ popup.find_usage=查找用例
popup.go_to_declaration=跳到声明
popup.exclude=排除
popup.rename=改名
#popup.search=
#popup.search_global=
confirm.save_as_title=确认另存为
confirm.save_as_message=%s 已存在。\n你想替换它吗?
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册