feat(gui): add auto complete for jadx scripts
This commit is contained in:
@@ -8,6 +8,12 @@ plugins {
|
||||
dependencies {
|
||||
implementation(project(':jadx-core'))
|
||||
implementation(project(":jadx-cli"))
|
||||
|
||||
// jadx-script autocomplete support
|
||||
implementation(project(":jadx-plugins::jadx-script:jadx-script-ide"))
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-common:1.7.20")
|
||||
implementation 'com.fifesoft:autocomplete:3.3.0'
|
||||
|
||||
implementation 'com.beust:jcommander:1.82'
|
||||
implementation 'ch.qos.logback:logback-classic:1.3.5'
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package jadx.gui.plugins.script;
|
||||
|
||||
import javax.swing.JList;
|
||||
|
||||
import org.fife.ui.autocomplete.Completion;
|
||||
import org.fife.ui.autocomplete.CompletionCellRenderer;
|
||||
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
|
||||
import static jadx.gui.utils.UiUtils.escapeHtml;
|
||||
import static jadx.gui.utils.UiUtils.fadeHtml;
|
||||
import static jadx.gui.utils.UiUtils.wrapHtml;
|
||||
|
||||
public class CompletionRenderer extends CompletionCellRenderer {
|
||||
|
||||
public CompletionRenderer(JadxSettings settings) {
|
||||
setDisplayFont(settings.getFont());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareForOtherCompletion(JList list, Completion c, int index, boolean selected, boolean hasFocus) {
|
||||
JadxScriptCompletion cmpl = (JadxScriptCompletion) c;
|
||||
setText(wrapHtml(escapeHtml(cmpl.getInputText()) + " "
|
||||
+ fadeHtml(escapeHtml(cmpl.getSummary()))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package jadx.gui.plugins.script;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
import org.fife.ui.autocomplete.Completion;
|
||||
import org.fife.ui.autocomplete.CompletionProviderBase;
|
||||
import org.fife.ui.autocomplete.ParameterizedCompletion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic;
|
||||
import kotlin.script.experimental.api.SourceCode;
|
||||
import kotlin.script.experimental.api.SourceCodeCompletionVariant;
|
||||
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.plugins.script.ide.JadxScriptAutoComplete;
|
||||
import jadx.plugins.script.ide.ScriptCompletionResult;
|
||||
|
||||
import static jadx.plugins.script.ide.CompleteKt.AUTO_COMPLETE_INSERT_STR;
|
||||
|
||||
public class JadxScriptCompleteProvider extends CompletionProviderBase {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxScriptCompleteProvider.class);
|
||||
|
||||
private static final Map<String, Icon> ICONS_MAP = buildIconsMap();
|
||||
|
||||
private static Map<String, Icon> buildIconsMap() {
|
||||
Map<String, Icon> map = new HashMap<>();
|
||||
map.put("class", Icons.CLASS);
|
||||
map.put("method", Icons.METHOD);
|
||||
map.put("field", Icons.FIELD);
|
||||
map.put("property", Icons.PROPERTY);
|
||||
map.put("parameter", Icons.PARAMETER);
|
||||
map.put("package", Icons.PACKAGE);
|
||||
return map;
|
||||
}
|
||||
|
||||
private final AbstractCodeArea codeArea;
|
||||
|
||||
public JadxScriptCompleteProvider(AbstractCodeArea codeArea) {
|
||||
this.codeArea = codeArea;
|
||||
}
|
||||
|
||||
private List<Completion> getCompletions() {
|
||||
try {
|
||||
String code = codeArea.getText();
|
||||
int caretPos = codeArea.getCaretPosition();
|
||||
JadxScriptAutoComplete scriptComplete = new JadxScriptAutoComplete(codeArea.getNode().getName());
|
||||
ScriptCompletionResult result = scriptComplete.complete(code, caretPos);
|
||||
int replacePos = getReplacePos(caretPos, result);
|
||||
if (!result.getReports().isEmpty()) {
|
||||
LOG.debug("Script completion reports: {}", result.getReports());
|
||||
}
|
||||
return convertCompletions(result.getCompletions(), code, replacePos);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Code completion failed", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Completion> convertCompletions(List<SourceCodeCompletionVariant> completions, String code, int replacePos) {
|
||||
if (completions.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
String cmplStr = completions.stream().map(SourceCodeCompletionVariant::toString).collect(Collectors.joining("\n"));
|
||||
LOG.debug("Completions:\n{}", cmplStr);
|
||||
}
|
||||
int count = completions.size();
|
||||
List<Completion> list = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
SourceCodeCompletionVariant c = completions.get(i);
|
||||
if (Objects.equals(c.getIcon(), "keyword")) {
|
||||
// too many, not very useful
|
||||
continue;
|
||||
}
|
||||
|
||||
JadxScriptCompletion cmpl = new JadxScriptCompletion(this, count - i);
|
||||
cmpl.setData(c.getText(), code, replacePos);
|
||||
if (Objects.equals(c.getIcon(), "method") && !Objects.equals(c.getText(), c.getDisplayText())) {
|
||||
// add method args details for methods
|
||||
cmpl.setSummary(c.getDisplayText() + " " + c.getTail());
|
||||
} else {
|
||||
cmpl.setSummary(c.getTail());
|
||||
}
|
||||
cmpl.setToolTip(c.getDisplayText());
|
||||
Icon icon = ICONS_MAP.get(c.getIcon());
|
||||
cmpl.setIcon(icon != null ? icon : Icons.FILE);
|
||||
list.add(cmpl);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private int getReplacePos(int caretPos, ScriptCompletionResult result) throws BadLocationException {
|
||||
int lineRaw = codeArea.getLineOfOffset(caretPos);
|
||||
int lineStart = codeArea.getLineStartOffset(lineRaw);
|
||||
int line = lineRaw + 1;
|
||||
int col = caretPos - lineStart + 1;
|
||||
|
||||
List<ScriptDiagnostic> reports = result.getReports();
|
||||
ScriptDiagnostic cmplReport = ListUtils.filterOnlyOne(reports, r -> {
|
||||
if (r.getSeverity() == ScriptDiagnostic.Severity.ERROR && r.getLocation() != null) {
|
||||
SourceCode.Position start = r.getLocation().getStart();
|
||||
return start.getLine() == line && r.getMessage().endsWith(AUTO_COMPLETE_INSERT_STR);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (cmplReport == null) {
|
||||
LOG.warn("Failed to find completion report in: {}", reports);
|
||||
return caretPos;
|
||||
}
|
||||
reports.remove(cmplReport);
|
||||
int reportCol = Objects.requireNonNull(cmplReport.getLocation()).getStart().getCol();
|
||||
return caretPos - (col - reportCol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlreadyEnteredText(JTextComponent comp) {
|
||||
try {
|
||||
int pos = codeArea.getCaretPosition();
|
||||
return codeArea.getText(0, pos);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to get text before caret", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Completion> getCompletionsAt(JTextComponent comp, Point p) {
|
||||
return getCompletions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Completion> getCompletionsImpl(JTextComponent comp) {
|
||||
return getCompletions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ParameterizedCompletion> getParameterizedCompletions(JTextComponent tc) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package jadx.gui.plugins.script;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
import org.fife.ui.autocomplete.Completion;
|
||||
import org.fife.ui.autocomplete.CompletionProvider;
|
||||
|
||||
public class JadxScriptCompletion implements Completion {
|
||||
|
||||
private final CompletionProvider provider;
|
||||
private final int relevance;
|
||||
|
||||
private String input;
|
||||
private String code;
|
||||
private int replacePos;
|
||||
private Icon icon;
|
||||
private String summary;
|
||||
private String toolTip;
|
||||
|
||||
public JadxScriptCompletion(CompletionProvider provider, int relevance) {
|
||||
this.provider = provider;
|
||||
this.relevance = relevance;
|
||||
}
|
||||
|
||||
public void setData(String input, String code, int replacePos) {
|
||||
this.input = input;
|
||||
this.code = code;
|
||||
this.replacePos = replacePos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInputText() {
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlreadyEntered(JTextComponent comp) {
|
||||
return provider.getAlreadyEnteredText(comp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRelevance() {
|
||||
return relevance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplacementText() {
|
||||
return code.substring(0, replacePos) + input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(Icon icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText() {
|
||||
return toolTip;
|
||||
}
|
||||
|
||||
public void setToolTip(String toolTip) {
|
||||
this.toolTip = toolTip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Completion other) {
|
||||
return Integer.compare(relevance, other.getRelevance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,13 @@ import jadx.gui.ui.codearea.ClassCodeContentPanel;
|
||||
import jadx.gui.ui.dialog.RenameDialog;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class JClass extends JLoadableNode {
|
||||
private static final long serialVersionUID = -1239986875244097177L;
|
||||
|
||||
private static final ImageIcon ICON_CLASS = UiUtils.openSvgIcon("nodes/class");
|
||||
private static final ImageIcon ICON_CLASS_ABSTRACT = UiUtils.openSvgIcon("nodes/abstractClass");
|
||||
private static final ImageIcon ICON_CLASS_PUBLIC = UiUtils.openSvgIcon("nodes/publicClass");
|
||||
private static final ImageIcon ICON_CLASS_PRIVATE = UiUtils.openSvgIcon("nodes/privateClass");
|
||||
@@ -155,7 +155,7 @@ public class JClass extends JLoadableNode {
|
||||
if (accessInfo.isPublic()) {
|
||||
return ICON_CLASS_PUBLIC;
|
||||
}
|
||||
return ICON_CLASS;
|
||||
return Icons.CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,13 +15,12 @@ import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.dialog.RenameDialog;
|
||||
import jadx.gui.utils.OverlayIcon;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class JField extends JNode {
|
||||
private static final long serialVersionUID = 1712572192106793359L;
|
||||
|
||||
private static final ImageIcon ICON_FLD_DEF = UiUtils.openSvgIcon("nodes/field");
|
||||
private static final ImageIcon ICON_FLD_PRI = UiUtils.openSvgIcon("nodes/privateField");
|
||||
private static final ImageIcon ICON_FLD_PRO = UiUtils.openSvgIcon("nodes/protectedField");
|
||||
private static final ImageIcon ICON_FLD_PUB = UiUtils.openSvgIcon("nodes/publicField");
|
||||
@@ -65,8 +64,7 @@ public class JField extends JNode {
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
AccessInfo af = field.getAccessFlags();
|
||||
OverlayIcon icon = UiUtils.makeIcon(af, ICON_FLD_PUB, ICON_FLD_PRI, ICON_FLD_PRO, ICON_FLD_DEF);
|
||||
return icon;
|
||||
return UiUtils.makeIcon(af, ICON_FLD_PUB, ICON_FLD_PRI, ICON_FLD_PRO, Icons.FIELD);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,8 +23,6 @@ import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class JMethod extends JNode {
|
||||
private static final long serialVersionUID = 3834526867464663751L;
|
||||
|
||||
private static final ImageIcon ICON_METHOD = UiUtils.openSvgIcon("nodes/method");
|
||||
private static final ImageIcon ICON_METHOD_ABSTRACT = UiUtils.openSvgIcon("nodes/abstractMethod");
|
||||
private static final ImageIcon ICON_METHOD_PRIVATE = UiUtils.openSvgIcon("nodes/privateMethod");
|
||||
private static final ImageIcon ICON_METHOD_PROTECTED = UiUtils.openSvgIcon("nodes/protectedMethod");
|
||||
@@ -66,7 +64,7 @@ public class JMethod extends JNode {
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
AccessInfo accessFlags = mth.getAccessFlags();
|
||||
Icon icon = ICON_METHOD;
|
||||
Icon icon = Icons.METHOD;
|
||||
if (accessFlags.isAbstract()) {
|
||||
icon = ICON_METHOD_ABSTRACT;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import jadx.api.JavaPackage;
|
||||
@@ -13,13 +12,11 @@ import jadx.core.utils.Utils;
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.popupmenu.JPackagePopupMenu;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.Icons;
|
||||
|
||||
public class JPackage extends JNode {
|
||||
private static final long serialVersionUID = -4120718634156839804L;
|
||||
|
||||
private static final ImageIcon PACKAGE_ICON = UiUtils.openSvgIcon("nodes/package");
|
||||
|
||||
private String fullName;
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
@@ -118,7 +115,7 @@ public class JPackage extends JNode {
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return PACKAGE_ICON;
|
||||
return Icons.PACKAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,7 @@ import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.CaretEvent;
|
||||
import javax.swing.event.CaretListener;
|
||||
@@ -29,6 +30,7 @@ import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Caret;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
|
||||
import org.fife.ui.autocomplete.AutoCompletion;
|
||||
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.Token;
|
||||
@@ -44,9 +46,12 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.plugins.script.CompletionRenderer;
|
||||
import jadx.gui.plugins.script.JadxScriptCompleteProvider;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JEditableNode;
|
||||
import jadx.gui.treemodel.JInputScript;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
@@ -101,6 +106,9 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
JEditableNode editableNode = (JEditableNode) node;
|
||||
addSaveActions(editableNode);
|
||||
addChangeUpdates(editableNode);
|
||||
if (node instanceof JInputScript) {
|
||||
addAutoComplete(settings);
|
||||
}
|
||||
} else {
|
||||
addCaretActions();
|
||||
addFastCopyAction();
|
||||
@@ -216,6 +224,17 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
}));
|
||||
}
|
||||
|
||||
private void addAutoComplete(JadxSettings settings) {
|
||||
JadxScriptCompleteProvider provider = new JadxScriptCompleteProvider(this);
|
||||
provider.setAutoActivationRules(false, ".");
|
||||
AutoCompletion ac = new AutoCompletion(provider);
|
||||
ac.setListCellRenderer(new CompletionRenderer(settings));
|
||||
ac.setTriggerKey(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, UiUtils.ctrlButton()));
|
||||
ac.setAutoActivationEnabled(true);
|
||||
ac.setAutoCompleteSingleChoices(true);
|
||||
ac.install(this);
|
||||
}
|
||||
|
||||
private String highlightCaretWord(String lastText, int pos) {
|
||||
String text = getWordByPosition(pos);
|
||||
if (StringUtils.isEmpty(text)) {
|
||||
|
||||
@@ -18,7 +18,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JDialog;
|
||||
@@ -33,12 +32,12 @@ import javax.swing.tree.TreePath;
|
||||
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class ExcludePkgDialog extends JDialog {
|
||||
private static final long serialVersionUID = -1111111202104151030L;
|
||||
private static final ImageIcon PACKAGE_ICON = UiUtils.openSvgIcon("nodes/package");
|
||||
|
||||
private final transient MainWindow mainWindow;
|
||||
private transient JTree tree;
|
||||
@@ -311,7 +310,7 @@ public class ExcludePkgDialog extends JDialog {
|
||||
return node.checkbox;
|
||||
}
|
||||
Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
|
||||
setIcon(PACKAGE_ICON);
|
||||
setIcon(Icons.PACKAGE);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,11 @@ public class Icons {
|
||||
|
||||
public static final ImageIcon FOLDER = UiUtils.openSvgIcon("nodes/folder");
|
||||
public static final ImageIcon FILE = UiUtils.openSvgIcon("nodes/file_any_type");
|
||||
|
||||
public static final ImageIcon PACKAGE = UiUtils.openSvgIcon("nodes/package");
|
||||
public static final ImageIcon CLASS = UiUtils.openSvgIcon("nodes/class");
|
||||
public static final ImageIcon METHOD = UiUtils.openSvgIcon("nodes/method");
|
||||
public static final ImageIcon FIELD = UiUtils.openSvgIcon("nodes/field");
|
||||
public static final ImageIcon PROPERTY = UiUtils.openSvgIcon("nodes/property");
|
||||
public static final ImageIcon PARAMETER = UiUtils.openSvgIcon("nodes/parameter");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<!-- Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#F4AF3D" fill-opacity=".6" d="M15,8 C15,11.866 11.866,15 8,15 C4.134,15 1,11.866 1,8 C1,4.134 4.134,1 8,1 C11.866,1 15,4.134 15,8"/>
|
||||
<path fill="#231F20" fill-opacity=".7" d="M0.9974,2.8021 C0.9974,3.83569336 1.23855513,4.8771 2.5354,4.8771 C3.83224487,4.8771 4,3.4641 4,2.7921 C4,2.1061 3.90228271,0.7461 2.5244,0.7461 C1.14651729,0.7461 0.9974,1.76850664 0.9974,2.8021 Z M1,5 C1.008,5.074 1.00713333,6.074 0.9974,8 L0.0004,8 L0.0004,0.0001 L0.9974,0.0001 L0.9974,0.7461 C1.36693099,0.248766667 1.93993099,0.0001 2.7164,0.0001 C3.88110352,0.0001 5.0004,0.578186035 5.0004,2.8021 C5.0004,5.02601396 3.65454102,5.6231 2.7164,5.6231 C2.09097266,5.6231 1.51883932,5.4154 1,5 Z" transform="translate(6 5)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,7 @@
|
||||
<!-- Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#B99BF8" fill-opacity=".6" d="M15,8 C15,11.866 11.866,15 8,15 C4.134,15 1,11.866 1,8 C1,4.134 4.134,1 8,1 C11.866,1 15,4.134 15,8"/>
|
||||
<path fill="#231F20" fill-opacity=".7" d="M0.9974,2.8021 C0.9974,3.83569336 1.23855513,4.8771 2.5354,4.8771 C3.83224487,4.8771 4,3.4641 4,2.7921 C4,2.1061 3.90228271,0.7461 2.5244,0.7461 C1.14651729,0.7461 0.9974,1.76850664 0.9974,2.8021 Z M1,5 C1.008,5.074 1.00713333,6.074 0.9974,8 L0.0004,8 L0.0004,0.0001 L0.9974,0.0001 L0.9974,0.7461 C1.36693099,0.248766667 1.93993099,0.0001 2.7164,0.0001 C3.88110352,0.0001 5.0004,0.578186035 5.0004,2.8021 C5.0004,5.02601396 3.65454102,5.6231 2.7164,5.6231 C2.09097266,5.6231 1.51883932,5.4154 1,5 Z" transform="translate(6 5)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.7.20"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-common")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-ide-services")
|
||||
|
||||
implementation(project(":jadx-plugins:jadx-script:jadx-script-runtime"))
|
||||
implementation(project(":jadx-plugins:jadx-script:jadx-script-plugin"))
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
|
||||
implementation("io.github.microutils:kotlin-logging-jvm:3.0.2")
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package jadx.plugins.script.ide
|
||||
|
||||
import jadx.plugins.script.runner.ScriptEval
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.KJvmReplCompilerWithIdeServices
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.toScriptSource
|
||||
import kotlin.script.experimental.jvm.util.toSourceCodePosition
|
||||
|
||||
const val AUTO_COMPLETE_INSERT_STR = "ABCDEF" // defined at KJvmReplCompleter.INSERTED_STRING
|
||||
|
||||
data class ScriptCompletionResult(
|
||||
val completions: List<SourceCodeCompletionVariant>,
|
||||
val reports: List<ScriptDiagnostic>
|
||||
)
|
||||
|
||||
class JadxScriptAutoComplete(private val scriptName: String) {
|
||||
private val replCompiler = KJvmReplCompilerWithIdeServices()
|
||||
private val compileConf = ScriptEval().buildCompileConf()
|
||||
|
||||
fun complete(code: String, cursor: Int): ScriptCompletionResult {
|
||||
val result = complete(code.toScriptSource(scriptName), cursor)
|
||||
return ScriptCompletionResult(
|
||||
completions = result.valueOrNull()?.toList() ?: listOf(),
|
||||
reports = result.reports
|
||||
)
|
||||
}
|
||||
|
||||
private fun complete(code: SourceCode, cursor: Int): ResultWithDiagnostics<ReplCompletionResult> {
|
||||
return runBlocking {
|
||||
replCompiler.complete(code, cursor.toSourceCodePosition(code), compileConf)
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -2,7 +2,6 @@ package jadx.plugins.script.runner
|
||||
|
||||
import jadx.api.JadxDecompiler
|
||||
import jadx.api.plugins.JadxPluginContext
|
||||
import jadx.api.plugins.pass.JadxPassContext
|
||||
import jadx.plugins.script.runtime.JadxScript
|
||||
import jadx.plugins.script.runtime.JadxScriptData
|
||||
import mu.KotlinLogging
|
||||
@@ -38,11 +37,17 @@ class ScriptEval {
|
||||
processEvalResult(result, scriptFile)
|
||||
}
|
||||
|
||||
private fun eval(scriptFile: File, scriptData: JadxScriptData): ResultWithDiagnostics<EvaluationResult> {
|
||||
val compilationConf = createJvmCompilationConfigurationFromTemplate<JadxScript>()
|
||||
val evalConf = createJvmEvaluationConfigurationFromTemplate<JadxScript> {
|
||||
fun buildCompileConf() = createJvmCompilationConfigurationFromTemplate<JadxScript>()
|
||||
|
||||
fun buildEvalConf(scriptData: JadxScriptData): ScriptEvaluationConfiguration {
|
||||
return createJvmEvaluationConfigurationFromTemplate<JadxScript> {
|
||||
constructorArgs(scriptData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun eval(scriptFile: File, scriptData: JadxScriptData): ResultWithDiagnostics<EvaluationResult> {
|
||||
val compilationConf = buildCompileConf()
|
||||
val evalConf = buildEvalConf(scriptData)
|
||||
return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), compilationConf, evalConf)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ plugins {
|
||||
kotlin("jvm") version "1.7.20"
|
||||
}
|
||||
|
||||
group = "jadx-script-context"
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-common")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm")
|
||||
|
||||
@@ -14,4 +14,5 @@ include("jadx-plugins:jadx-java-convert")
|
||||
|
||||
include("jadx-plugins:jadx-script:jadx-script-plugin")
|
||||
include("jadx-plugins:jadx-script:jadx-script-runtime")
|
||||
include("jadx-plugins:jadx-script:jadx-script-ide")
|
||||
include("jadx-plugins:jadx-script:examples")
|
||||
|
||||
Reference in New Issue
Block a user