feat(gui): add run, check and format script actions
This commit is contained in:
+8
-1
@@ -4,7 +4,7 @@ plugins {
|
||||
id 'com.github.ben-manes.versions' version '0.45.0'
|
||||
id 'com.diffplug.spotless' version '6.13.0'
|
||||
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.7.20' // needed for spotless
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.7.20'
|
||||
}
|
||||
|
||||
ext.jadxVersion = System.getenv('JADX_VERSION') ?: "dev"
|
||||
@@ -23,6 +23,12 @@ allprojects {
|
||||
compileJava {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
incremental = false // cause unexpected issues sometime
|
||||
// useK2 = true
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
@@ -78,6 +84,7 @@ spotless {
|
||||
kotlin {
|
||||
target fileTree(rootDir).matching {
|
||||
include 'jadx-plugins/jadx-script/**/*.kt'
|
||||
include 'jadx-gui/src/main/**/*.kt'
|
||||
// include 'jadx-plugins/jadx-script/examples/scripts/**/*.jadx.kts'
|
||||
}
|
||||
ktlint()
|
||||
|
||||
@@ -128,6 +128,17 @@ public final class JadxDecompiler implements Closeable {
|
||||
loadFinished();
|
||||
}
|
||||
|
||||
public void reloadPasses() {
|
||||
LOG.info("reloading (passes only) ...");
|
||||
customPasses.clear();
|
||||
root.resetPasses();
|
||||
loadPlugins();
|
||||
root.mergePasses(customPasses);
|
||||
root.restartVisitors();
|
||||
root.initPasses();
|
||||
loadFinished();
|
||||
}
|
||||
|
||||
private void loadInputFiles() {
|
||||
loadedInputs.clear();
|
||||
List<Path> inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath);
|
||||
|
||||
@@ -350,7 +350,7 @@ public class ClassNode extends NotificationAttrNode
|
||||
innerClasses.forEach(ClassNode::deepUnload);
|
||||
}
|
||||
|
||||
private void unloadFromCache() {
|
||||
public void unloadFromCache() {
|
||||
if (isInner()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -343,6 +343,24 @@ public class RootNode {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make better API for reload passes lists
|
||||
public void resetPasses() {
|
||||
preDecompilePasses.clear();
|
||||
preDecompilePasses.addAll(Jadx.getPreDecompilePassesList());
|
||||
|
||||
processClasses.getPasses().clear();
|
||||
processClasses.getPasses().addAll(Jadx.getPassesList(args));
|
||||
}
|
||||
|
||||
public void restartVisitors() {
|
||||
for (ClassNode cls : classes) {
|
||||
cls.unload();
|
||||
cls.clearAttributes();
|
||||
cls.unloadFromCache();
|
||||
}
|
||||
runPreDecompileStage();
|
||||
}
|
||||
|
||||
public List<ClassNode> getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
+13
-6
@@ -14,9 +14,13 @@ dependencies {
|
||||
|
||||
// jadx-script autocomplete support
|
||||
implementation(project(":jadx-plugins::jadx-script:jadx-script-ide"))
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-common:1.7.20")
|
||||
implementation 'org.jetbrains.kotlin:kotlin-scripting-common:1.7.20'
|
||||
implementation 'com.fifesoft:autocomplete:3.3.0'
|
||||
|
||||
// use ktlint for lint and format jadx scripts
|
||||
implementation 'com.pinterest.ktlint:ktlint-core:0.47.1'
|
||||
implementation 'com.pinterest.ktlint:ktlint-ruleset-standard:0.47.1'
|
||||
|
||||
implementation 'com.beust:jcommander:1.82'
|
||||
implementation 'ch.qos.logback:logback-classic:1.3.5'
|
||||
|
||||
@@ -44,10 +48,13 @@ dependencies {
|
||||
application {
|
||||
applicationName = 'jadx-gui'
|
||||
mainClass.set('jadx.gui.JadxGUI')
|
||||
// The option -XX:+UseG1GC is only relevant for Java 8. Starting with Java 9 G1GC is already the default GC
|
||||
applicationDefaultJvmArgs = ['-Xms128M', '-XX:MaxRAMPercentage=70.0', '-XX:+UseG1GC',
|
||||
'-Dawt.useSystemAAFontSettings=lcd', '-Dswing.aatext=true',
|
||||
'-Djava.util.Arrays.useLegacyMergeSort=true']
|
||||
applicationDefaultJvmArgs = [
|
||||
'-Xms128M', '-XX:MaxRAMPercentage=70.0',
|
||||
'-XX:+UseG1GC', // only relevant for Java 8, starting with Java 9 G1GC is already the default GC
|
||||
'-Dawt.useSystemAAFontSettings=lcd', '-Dswing.aatext=true',
|
||||
'-Djava.util.Arrays.useLegacyMergeSort=true',
|
||||
'-XX:+IgnoreUnrecognizedVMOptions', '--add-opens=java.base/java.lang=ALL-UNNAMED', // for ktlint formatter
|
||||
]
|
||||
}
|
||||
|
||||
applicationDistribution.with {
|
||||
@@ -94,7 +101,7 @@ launch4j {
|
||||
windowTitle = 'jadx'
|
||||
companyName = 'jadx'
|
||||
jreMinVersion = '11'
|
||||
jvmOptions = ['-Dawt.useSystemAAFontSettings=lcd', '-Dswing.aatext=true', '-XX:+UseG1GC', '-Djava.util.Arrays.useLegacyMergeSort=true']
|
||||
jvmOptions = application.getApplicationDefaultJvmArgs()
|
||||
jreRuntimeBits = "64"
|
||||
bundledJre64Bit = true
|
||||
initialHeapPercent = 5
|
||||
|
||||
@@ -182,9 +182,9 @@ public class DiskCodeCache implements ICodeCache {
|
||||
@Override
|
||||
public void remove(String clsFullName) {
|
||||
try {
|
||||
LOG.debug("Removing class info from disk: {}", clsFullName);
|
||||
Integer clsId = namesMap.remove(clsFullName);
|
||||
if (clsId != null) {
|
||||
LOG.debug("Removing class info from disk: {}", clsFullName);
|
||||
Files.deleteIfExists(getJavaFile(clsId));
|
||||
Files.deleteIfExists(getMetadataFile(clsId));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package jadx.gui.plugins.script
|
||||
|
||||
import com.pinterest.ktlint.core.KtLint
|
||||
import com.pinterest.ktlint.core.LintError
|
||||
import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
object KtLintUtils {
|
||||
|
||||
val LOG: Logger = LoggerFactory.getLogger(KtLintUtils::class.java)
|
||||
|
||||
val rules = lazy { StandardRuleSetProvider().getRuleProviders() }
|
||||
|
||||
fun format(code: String, fileName: String): String {
|
||||
val params = KtLint.ExperimentalParams(
|
||||
text = code,
|
||||
fileName = fileName,
|
||||
ruleProviders = rules.value,
|
||||
script = true,
|
||||
cb = { e: LintError, corrected ->
|
||||
if (!corrected) {
|
||||
LOG.warn("Lint error: {}", e)
|
||||
}
|
||||
}
|
||||
)
|
||||
return KtLint.format(params)
|
||||
}
|
||||
|
||||
fun lint(code: String, fileName: String): List<LintError> {
|
||||
val errors = mutableListOf<LintError>()
|
||||
val params = KtLint.ExperimentalParams(
|
||||
text = code,
|
||||
fileName = fileName,
|
||||
ruleProviders = rules.value,
|
||||
script = true,
|
||||
cb = { e: LintError, corrected ->
|
||||
if (!corrected) {
|
||||
errors.add(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
KtLint.lint(params)
|
||||
return errors
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package jadx.gui.plugins.script;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.fife.ui.autocomplete.AutoCompletion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JInputScript;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class ScriptCodeArea extends AbstractCodeArea {
|
||||
|
||||
private final JInputScript scriptNode;
|
||||
private final AutoCompletion autoCompletion;
|
||||
|
||||
public ScriptCodeArea(ContentPanel contentPanel, JInputScript node) {
|
||||
super(contentPanel, node);
|
||||
scriptNode = node;
|
||||
|
||||
setSyntaxEditingStyle(node.getSyntaxName());
|
||||
setCodeFoldingEnabled(true);
|
||||
setCloseCurlyBraces(true);
|
||||
|
||||
JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
|
||||
autoCompletion = addAutoComplete(settings);
|
||||
}
|
||||
|
||||
private AutoCompletion addAutoComplete(JadxSettings settings) {
|
||||
ScriptCompleteProvider provider = new ScriptCompleteProvider(this);
|
||||
provider.setAutoActivationRules(false, ".");
|
||||
AutoCompletion ac = new AutoCompletion(provider);
|
||||
ac.setListCellRenderer(new ScriptCompletionRenderer(settings));
|
||||
ac.setTriggerKey(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, UiUtils.ctrlButton()));
|
||||
ac.setAutoActivationEnabled(true);
|
||||
ac.setAutoCompleteSingleChoices(true);
|
||||
ac.install(this);
|
||||
return ac;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ICodeInfo getCodeInfo() {
|
||||
return node.getCodeInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (getText().isEmpty()) {
|
||||
setText(getCodeInfo().getCodeStr());
|
||||
setCaretPosition(0);
|
||||
setLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
setText(node.getCodeInfo().getCodeStr());
|
||||
}
|
||||
|
||||
public void updateCode(String newCode) {
|
||||
setText(newCode);
|
||||
scriptNode.setChanged(true);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
scriptNode.save(getText());
|
||||
scriptNode.setChanged(false);
|
||||
}
|
||||
|
||||
public JInputScript getScriptNode() {
|
||||
return scriptNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
autoCompletion.uninstall();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
+10
-7
@@ -27,13 +27,13 @@ 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.ScriptCompiler;
|
||||
import jadx.plugins.script.ide.ScriptCompletionResult;
|
||||
|
||||
import static jadx.plugins.script.ide.CompleteKt.AUTO_COMPLETE_INSERT_STR;
|
||||
import static jadx.plugins.script.ide.ScriptCompilerKt.AUTO_COMPLETE_INSERT_STR;
|
||||
|
||||
public class JadxScriptCompleteProvider extends CompletionProviderBase {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxScriptCompleteProvider.class);
|
||||
public class ScriptCompleteProvider extends CompletionProviderBase {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ScriptCompleteProvider.class);
|
||||
|
||||
private static final Map<String, Icon> ICONS_MAP = buildIconsMap();
|
||||
|
||||
@@ -49,16 +49,19 @@ public class JadxScriptCompleteProvider extends CompletionProviderBase {
|
||||
}
|
||||
|
||||
private final AbstractCodeArea codeArea;
|
||||
private ScriptCompiler scriptComplete;
|
||||
|
||||
public JadxScriptCompleteProvider(AbstractCodeArea codeArea) {
|
||||
public ScriptCompleteProvider(AbstractCodeArea codeArea) {
|
||||
this.codeArea = codeArea;
|
||||
// this.scriptComplete = new ScriptCompiler(codeArea.getNode().getName());
|
||||
}
|
||||
|
||||
private List<Completion> getCompletions() {
|
||||
try {
|
||||
String code = codeArea.getText();
|
||||
int caretPos = codeArea.getCaretPosition();
|
||||
JadxScriptAutoComplete scriptComplete = new JadxScriptAutoComplete(codeArea.getNode().getName());
|
||||
// TODO: resolve error after reusing ScriptCompiler
|
||||
scriptComplete = new ScriptCompiler(codeArea.getNode().getName());
|
||||
ScriptCompletionResult result = scriptComplete.complete(code, caretPos);
|
||||
int replacePos = getReplacePos(caretPos, result);
|
||||
if (!result.getReports().isEmpty()) {
|
||||
@@ -88,7 +91,7 @@ public class JadxScriptCompleteProvider extends CompletionProviderBase {
|
||||
continue;
|
||||
}
|
||||
|
||||
JadxScriptCompletion cmpl = new JadxScriptCompletion(this, count - i);
|
||||
ScriptCompletionData cmpl = new ScriptCompletionData(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
|
||||
+2
-2
@@ -6,7 +6,7 @@ import javax.swing.text.JTextComponent;
|
||||
import org.fife.ui.autocomplete.Completion;
|
||||
import org.fife.ui.autocomplete.CompletionProvider;
|
||||
|
||||
public class JadxScriptCompletion implements Completion {
|
||||
public class ScriptCompletionData implements Completion {
|
||||
|
||||
private final CompletionProvider provider;
|
||||
private final int relevance;
|
||||
@@ -18,7 +18,7 @@ public class JadxScriptCompletion implements Completion {
|
||||
private String summary;
|
||||
private String toolTip;
|
||||
|
||||
public JadxScriptCompletion(CompletionProvider provider, int relevance) {
|
||||
public ScriptCompletionData(CompletionProvider provider, int relevance) {
|
||||
this.provider = provider;
|
||||
this.relevance = relevance;
|
||||
}
|
||||
+3
-3
@@ -11,15 +11,15 @@ 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 class ScriptCompletionRenderer extends CompletionCellRenderer {
|
||||
|
||||
public CompletionRenderer(JadxSettings settings) {
|
||||
public ScriptCompletionRenderer(JadxSettings settings) {
|
||||
setDisplayFont(settings.getFont());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareForOtherCompletion(JList list, Completion c, int index, boolean selected, boolean hasFocus) {
|
||||
JadxScriptCompletion cmpl = (JadxScriptCompletion) c;
|
||||
ScriptCompletionData cmpl = (ScriptCompletionData) c;
|
||||
setText(wrapHtml(escapeHtml(cmpl.getInputText()) + " "
|
||||
+ fadeHtml(escapeHtml(cmpl.getSummary()))));
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
package jadx.gui.plugins.script;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.ErrorStrip;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.pinterest.ktlint.core.LintError;
|
||||
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic;
|
||||
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.LineNumbersMode;
|
||||
import jadx.gui.treemodel.JInputScript;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
|
||||
import jadx.gui.ui.codearea.SearchBar;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.ui.ActionHandler;
|
||||
import jadx.gui.utils.ui.NodeLabel;
|
||||
import jadx.plugins.script.ide.ScriptAnalyzeResult;
|
||||
import jadx.plugins.script.ide.ScriptCompiler;
|
||||
|
||||
public class ScriptContentPanel extends AbstractCodeContentPanel {
|
||||
private static final long serialVersionUID = 6575696321112417513L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ScriptContentPanel.class);
|
||||
|
||||
private final ScriptCodeArea scriptArea;
|
||||
private final SearchBar searchBar;
|
||||
private final RTextScrollPane codeScrollPane;
|
||||
private final JPanel actionPanel;
|
||||
private final JLabel resultLabel;
|
||||
private final ScriptErrorService errorService;
|
||||
|
||||
public ScriptContentPanel(TabbedPane panel, JInputScript scriptNode) {
|
||||
super(panel, scriptNode);
|
||||
scriptArea = new ScriptCodeArea(this, scriptNode);
|
||||
resultLabel = new NodeLabel("");
|
||||
errorService = new ScriptErrorService(scriptArea);
|
||||
actionPanel = buildScriptActionsPanel();
|
||||
searchBar = new SearchBar(scriptArea);
|
||||
codeScrollPane = new RTextScrollPane(scriptArea);
|
||||
|
||||
initUI();
|
||||
applySettings();
|
||||
scriptArea.load();
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
JPanel topPanel = new JPanel(new BorderLayout());
|
||||
topPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
topPanel.add(actionPanel, BorderLayout.NORTH);
|
||||
topPanel.add(searchBar, BorderLayout.SOUTH);
|
||||
|
||||
JPanel codePanel = new JPanel(new BorderLayout());
|
||||
codePanel.setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
codePanel.add(codeScrollPane);
|
||||
codePanel.add(new ErrorStrip(scriptArea), BorderLayout.LINE_END);
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
add(topPanel, BorderLayout.NORTH);
|
||||
add(codeScrollPane, BorderLayout.CENTER);
|
||||
|
||||
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_F, UiUtils.ctrlButton());
|
||||
UiUtils.addKeyBinding(scriptArea, key, "SearchAction", searchBar::toggle);
|
||||
}
|
||||
|
||||
private JPanel buildScriptActionsPanel() {
|
||||
ActionHandler runAction = new ActionHandler(this::runScript);
|
||||
runAction.setNameAndDesc(NLS.str("script.run"));
|
||||
runAction.setIcon(Icons.RUN);
|
||||
runAction.attachKeyBindingFor(scriptArea, KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0));
|
||||
|
||||
ActionHandler saveAction = new ActionHandler(scriptArea::save);
|
||||
saveAction.setNameAndDesc(NLS.str("script.save"));
|
||||
saveAction.setIcon(Icons.SAVE_ALL);
|
||||
saveAction.setKeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_S, UiUtils.ctrlButton()));
|
||||
|
||||
JButton save = saveAction.makeButton();
|
||||
scriptArea.getScriptNode().addChangeListener(save::setEnabled);
|
||||
|
||||
JButton check = new JButton(NLS.str("script.check"), Icons.CHECK);
|
||||
check.addActionListener(ev -> checkScript());
|
||||
JButton format = new JButton(NLS.str("script.format"), Icons.FORMAT);
|
||||
format.addActionListener(ev -> reformatCode());
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
|
||||
panel.setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
panel.add(runAction.makeButton());
|
||||
panel.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
panel.add(save);
|
||||
panel.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
panel.add(check);
|
||||
panel.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
panel.add(format);
|
||||
panel.add(Box.createRigidArea(new Dimension(30, 0)));
|
||||
panel.add(resultLabel);
|
||||
panel.add(Box.createHorizontalGlue());
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void runScript() {
|
||||
scriptArea.save();
|
||||
if (!checkScript()) {
|
||||
return;
|
||||
}
|
||||
resetResultLabel();
|
||||
|
||||
TabbedPane tabbedPane = getTabbedPane();
|
||||
MainWindow mainWindow = tabbedPane.getMainWindow();
|
||||
mainWindow.getBackgroundExecutor().execute(NLS.str("script.run"), () -> {
|
||||
try {
|
||||
mainWindow.getWrapper().getDecompiler().reloadPasses();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Passes reload failed", e);
|
||||
}
|
||||
}, taskStatus -> {
|
||||
tabbedPane.reloadInactiveTabs();
|
||||
mainWindow.reloadTree();
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkScript() {
|
||||
try {
|
||||
resetResultLabel();
|
||||
String code = scriptArea.getText();
|
||||
String fileName = scriptArea.getNode().getName();
|
||||
|
||||
ScriptCompiler scriptCompiler = new ScriptCompiler(fileName);
|
||||
ScriptAnalyzeResult result = scriptCompiler.analyze(code, scriptArea.getCaretPosition());
|
||||
List<ScriptDiagnostic> errors = result.getErrors();
|
||||
for (ScriptDiagnostic error : errors) {
|
||||
LOG.warn("Parse error: {}", error);
|
||||
}
|
||||
|
||||
List<LintError> lintErrs = Collections.emptyList();
|
||||
if (errors.isEmpty()) {
|
||||
lintErrs = getLintIssues(code, fileName);
|
||||
}
|
||||
|
||||
errorService.clearErrors();
|
||||
errorService.addErrors(errors);
|
||||
errorService.addLintErrors(lintErrs);
|
||||
errorService.apply();
|
||||
boolean success = errors.isEmpty();
|
||||
if (!success) {
|
||||
resultLabel.setText("Parsing errors: " + errors.size());
|
||||
} else if (!lintErrs.isEmpty()) {
|
||||
resultLabel.setText("Lint issues: " + lintErrs.size());
|
||||
}
|
||||
return success;
|
||||
} catch (Throwable e) {
|
||||
LOG.error("Failed to check code", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private List<LintError> getLintIssues(String code, String fileName) {
|
||||
try {
|
||||
List<LintError> lintErrs = KtLintUtils.INSTANCE.lint(code, fileName);
|
||||
for (LintError error : lintErrs) {
|
||||
LOG.warn("Lint issue: {}", error);
|
||||
}
|
||||
return lintErrs;
|
||||
} catch (Throwable e) { // can throw initialization error
|
||||
LOG.warn("KtLint failed", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private void reformatCode() {
|
||||
resetResultLabel();
|
||||
try {
|
||||
String code = scriptArea.getText();
|
||||
String fileName = scriptArea.getNode().getName();
|
||||
String formattedCode = KtLintUtils.INSTANCE.format(code, fileName);
|
||||
if (!code.equals(formattedCode)) {
|
||||
scriptArea.updateCode(formattedCode);
|
||||
resultLabel.setText("Code updated");
|
||||
errorService.clearErrors();
|
||||
}
|
||||
} catch (Throwable e) { // can throw initialization error
|
||||
LOG.error("Failed to reformat code", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetResultLabel() {
|
||||
resultLabel.setText("");
|
||||
}
|
||||
|
||||
private void applySettings() {
|
||||
JadxSettings settings = getSettings();
|
||||
codeScrollPane.setLineNumbersEnabled(settings.getLineNumbersMode() != LineNumbersMode.DISABLE);
|
||||
codeScrollPane.getGutter().setLineNumberFont(settings.getFont());
|
||||
scriptArea.loadSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractCodeArea getCodeArea() {
|
||||
return scriptArea;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadSettings() {
|
||||
applySettings();
|
||||
updateUI();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
scriptArea.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package jadx.gui.plugins.script;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
||||
import org.fife.ui.rsyntaxtextarea.parser.AbstractParser;
|
||||
import org.fife.ui.rsyntaxtextarea.parser.DefaultParseResult;
|
||||
import org.fife.ui.rsyntaxtextarea.parser.DefaultParserNotice;
|
||||
import org.fife.ui.rsyntaxtextarea.parser.ParseResult;
|
||||
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.pinterest.ktlint.core.LintError;
|
||||
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic;
|
||||
import kotlin.script.experimental.api.SourceCode;
|
||||
|
||||
public class ScriptErrorService extends AbstractParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ScriptErrorService.class);
|
||||
private final DefaultParseResult result;
|
||||
private final ScriptCodeArea scriptArea;
|
||||
|
||||
public ScriptErrorService(ScriptCodeArea scriptArea) {
|
||||
this.scriptArea = scriptArea;
|
||||
this.result = new DefaultParseResult(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParseResult parse(RSyntaxDocument doc, String style) {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void clearErrors() {
|
||||
result.clearNotices();
|
||||
scriptArea.removeParser(this);
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
scriptArea.removeParser(this);
|
||||
scriptArea.addParser(this);
|
||||
scriptArea.addNotify();
|
||||
jumpCaretToFirstError();
|
||||
}
|
||||
|
||||
private void jumpCaretToFirstError() {
|
||||
List<ParserNotice> parserNotices = result.getNotices();
|
||||
if (parserNotices.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ParserNotice notice = parserNotices.get(0);
|
||||
int offset = notice.getOffset();
|
||||
if (offset != -1) {
|
||||
scriptArea.setCaretPosition(offset);
|
||||
} else {
|
||||
try {
|
||||
scriptArea.setCaretPosition(scriptArea.getLineStartOffset(notice.getLine()));
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to jump to first error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addErrors(List<ScriptDiagnostic> errors) {
|
||||
for (ScriptDiagnostic error : errors) {
|
||||
DefaultParserNotice notice;
|
||||
SourceCode.Location loc = error.getLocation();
|
||||
if (loc == null) {
|
||||
notice = new DefaultParserNotice(this, error.getMessage(), 0);
|
||||
} else {
|
||||
try {
|
||||
int line = loc.getStart().getLine();
|
||||
int offset = scriptArea.getLineStartOffset(line - 1) + loc.getStart().getCol();
|
||||
int len = loc.getEnd() == null ? -1 : loc.getEnd().getCol() - loc.getStart().getCol();
|
||||
notice = new DefaultParserNotice(this, error.getMessage(), line, offset - 1, len);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to convert script error", e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
addNotice(notice);
|
||||
}
|
||||
}
|
||||
|
||||
public void addLintErrors(List<LintError> errors) {
|
||||
for (LintError error : errors) {
|
||||
try {
|
||||
int line = error.getLine();
|
||||
int offset = scriptArea.getLineStartOffset(line - 1) + error.getCol() - 1;
|
||||
String word = scriptArea.getWordByPosition(offset);
|
||||
int len = word != null ? word.length() : -1;
|
||||
DefaultParserNotice notice = new DefaultParserNotice(this, error.getDetail(), line, offset, len);
|
||||
notice.setLevel(ParserNotice.Level.WARNING);
|
||||
addNotice(notice);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to convert lint error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addNotice(DefaultParserNotice notice) {
|
||||
LOG.debug("Add notice: {}:{}:{} - {}",
|
||||
notice.getLine(), notice.getOffset(), notice.getLength(), notice.getMessage());
|
||||
result.addNotice(notice);
|
||||
}
|
||||
}
|
||||
@@ -31,5 +31,6 @@ public abstract class JEditableNode extends JNode {
|
||||
|
||||
public void addChangeListener(Consumer<Boolean> listener) {
|
||||
changeListeners.add(listener);
|
||||
listener.accept(changed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ import jadx.api.ICodeInfo;
|
||||
import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.gui.plugins.script.ScriptContentPanel;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.CodeContentPanel;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
@@ -38,7 +38,7 @@ public class JInputScript extends JEditableNode {
|
||||
|
||||
@Override
|
||||
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
|
||||
return new CodeContentPanel(tabbedPane, this);
|
||||
return new ScriptContentPanel(tabbedPane, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,11 +69,6 @@ public class JInputScript extends JEditableNode {
|
||||
return menu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntaxName() {
|
||||
return SyntaxConstants.SYNTAX_STYLE_KOTLIN;
|
||||
|
||||
@@ -163,7 +163,6 @@ public class MainWindow extends JFrame {
|
||||
public static final double SPLIT_PANE_RESIZE_WEIGHT = 0.15;
|
||||
|
||||
private static final ImageIcon ICON_ADD_FILES = UiUtils.openSvgIcon("ui/addFile");
|
||||
private static final ImageIcon ICON_SAVE_ALL = UiUtils.openSvgIcon("ui/menu-saveall");
|
||||
private static final ImageIcon ICON_RELOAD = UiUtils.openSvgIcon("ui/refresh");
|
||||
private static final ImageIcon ICON_EXPORT = UiUtils.openSvgIcon("ui/export");
|
||||
private static final ImageIcon ICON_EXIT = UiUtils.openSvgIcon("ui/exit");
|
||||
@@ -1083,7 +1082,7 @@ public class MainWindow extends JFrame {
|
||||
};
|
||||
closeMappingsAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.close_mappings"));
|
||||
|
||||
Action saveAllAction = new AbstractAction(NLS.str("file.save_all"), ICON_SAVE_ALL) {
|
||||
Action saveAllAction = new AbstractAction(NLS.str("file.save_all"), Icons.SAVE_ALL) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
saveAll(false);
|
||||
|
||||
@@ -33,6 +33,7 @@ import jadx.gui.ui.panel.ImagePanel;
|
||||
import jadx.gui.utils.JumpManager;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class TabbedPane extends JTabbedPane {
|
||||
private static final long serialVersionUID = -8833600618794570904L;
|
||||
@@ -332,7 +333,9 @@ public class TabbedPane extends JTabbedPane {
|
||||
|
||||
private void addContentPanel(ContentPanel contentPanel) {
|
||||
openTabs.put(contentPanel.getNode(), contentPanel);
|
||||
add(contentPanel);
|
||||
int tabCount = getTabCount();
|
||||
add(contentPanel, tabCount);
|
||||
setTabComponentAt(tabCount, makeTabComponent(contentPanel));
|
||||
}
|
||||
|
||||
public void closeCodePanel(ContentPanel contentPanel) {
|
||||
@@ -351,7 +354,6 @@ public class TabbedPane extends JTabbedPane {
|
||||
}
|
||||
FocusManager.listen(panel);
|
||||
addContentPanel(panel);
|
||||
setTabComponentAt(indexOfComponent(panel), makeTabComponent(panel));
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
@@ -364,6 +366,27 @@ public class TabbedPane extends JTabbedPane {
|
||||
}
|
||||
}
|
||||
|
||||
public void reloadInactiveTabs() {
|
||||
UiUtils.uiThreadGuard();
|
||||
int tabCount = getTabCount();
|
||||
if (tabCount == 1) {
|
||||
return;
|
||||
}
|
||||
int current = getSelectedIndex();
|
||||
for (int i = 0; i < tabCount; i++) {
|
||||
if (i == current) {
|
||||
continue;
|
||||
}
|
||||
JNode node = ((ContentPanel) getComponentAt(i)).getNode();
|
||||
ContentPanel panel = node.getContentPanel(this);
|
||||
FocusManager.listen(panel);
|
||||
openTabs.put(node, panel);
|
||||
setComponentAt(i, panel);
|
||||
setTabComponentAt(i, makeTabComponent(panel));
|
||||
}
|
||||
fireStateChanged();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ContentPanel getSelectedCodePanel() {
|
||||
return (ContentPanel) getSelectedComponent();
|
||||
|
||||
@@ -20,7 +20,6 @@ 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;
|
||||
@@ -30,7 +29,6 @@ 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;
|
||||
@@ -46,12 +44,9 @@ 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;
|
||||
@@ -106,9 +101,6 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
JEditableNode editableNode = (JEditableNode) node;
|
||||
addSaveActions(editableNode);
|
||||
addChangeUpdates(editableNode);
|
||||
if (node instanceof JInputScript) {
|
||||
addAutoComplete(settings);
|
||||
}
|
||||
} else {
|
||||
addCaretActions();
|
||||
addFastCopyAction();
|
||||
@@ -224,17 +216,6 @@ 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)) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.TextStandardActions;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
class SearchBar extends JToolBar {
|
||||
public class SearchBar extends JToolBar {
|
||||
private static final long serialVersionUID = 1836871286618633003L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SearchBar.class);
|
||||
|
||||
@@ -4,6 +4,7 @@ import javax.swing.JPanel;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
@@ -45,6 +46,10 @@ public abstract class ContentPanel extends JPanel {
|
||||
return node.getName();
|
||||
}
|
||||
|
||||
public JadxSettings getSettings() {
|
||||
return tabbedPane.getMainWindow().getSettings();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
tabbedPane = null;
|
||||
node = null;
|
||||
|
||||
@@ -13,6 +13,8 @@ public class Icons {
|
||||
public static final ImageIcon CLOSE = openSvgIcon("ui/closeHovered");
|
||||
public static final ImageIcon CLOSE_INACTIVE = openSvgIcon("ui/close");
|
||||
|
||||
public static final ImageIcon SAVE_ALL = UiUtils.openSvgIcon("ui/menu-saveall");
|
||||
|
||||
public static final ImageIcon STATIC = openSvgIcon("nodes/staticMark");
|
||||
public static final ImageIcon FINAL = openSvgIcon("nodes/finalMark");
|
||||
|
||||
@@ -27,4 +29,8 @@ public class Icons {
|
||||
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");
|
||||
|
||||
public static final ImageIcon RUN = UiUtils.openSvgIcon("ui/run");
|
||||
public static final ImageIcon CHECK = UiUtils.openSvgIcon("ui/checkConstraint");
|
||||
public static final ImageIcon FORMAT = UiUtils.openSvgIcon("ui/toolWindowMessages");
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package jadx.gui.utils.ui;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class ActionHandler extends AbstractAction {
|
||||
|
||||
private final Consumer<ActionEvent> consumer;
|
||||
@@ -40,8 +45,27 @@ public class ActionHandler extends AbstractAction {
|
||||
putValue(ACCELERATOR_KEY, keyStroke);
|
||||
}
|
||||
|
||||
public void attachKeyBindingFor(JComponent component, KeyStroke keyStroke) {
|
||||
UiUtils.addKeyBinding(component, keyStroke, "run", this);
|
||||
setKeyBinding(keyStroke);
|
||||
}
|
||||
|
||||
public void addKeyBindToDescription() {
|
||||
KeyStroke keyStroke = (KeyStroke) getValue(ACCELERATOR_KEY);
|
||||
if (keyStroke != null) {
|
||||
String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode());
|
||||
String desc = (String) getValue(SHORT_DESCRIPTION);
|
||||
setShortDescription(desc + " (" + keyText + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
consumer.accept(e);
|
||||
}
|
||||
|
||||
public JButton makeButton() {
|
||||
addKeyBindToDescription();
|
||||
return new JButton(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +248,11 @@ popup.search_global=Globale Suche "%s"
|
||||
#popup.add_scripts=Add scripts
|
||||
#popup.new_script=New script
|
||||
|
||||
#script.run=Run
|
||||
#script.save=Save
|
||||
#script.check=Check
|
||||
#script.format=Reformat
|
||||
|
||||
exclude_dialog.title=Paketauswahl
|
||||
exclude_dialog.ok=OK
|
||||
exclude_dialog.select_all=Alles auswählen
|
||||
|
||||
@@ -248,6 +248,11 @@ popup.add_files=Add files
|
||||
popup.add_scripts=Add scripts
|
||||
popup.new_script=New script
|
||||
|
||||
script.run=Run
|
||||
script.save=Save
|
||||
script.check=Check
|
||||
script.format=Reformat
|
||||
|
||||
exclude_dialog.title=Package Selector
|
||||
exclude_dialog.ok=OK
|
||||
exclude_dialog.select_all=Select all
|
||||
|
||||
@@ -248,6 +248,11 @@ popup.rename=Nimeta ümber
|
||||
#popup.add_scripts=Add scripts
|
||||
#popup.new_script=New script
|
||||
|
||||
#script.run=Run
|
||||
#script.save=Save
|
||||
#script.check=Check
|
||||
#script.format=Reformat
|
||||
|
||||
#exclude_dialog.title=Package Selector
|
||||
#exclude_dialog.ok=OK
|
||||
#exclude_dialog.select_all=Select all
|
||||
|
||||
@@ -248,6 +248,11 @@ popup.search_global="%s" 전역 검색
|
||||
#popup.add_scripts=Add scripts
|
||||
#popup.new_script=New script
|
||||
|
||||
#script.run=Run
|
||||
#script.save=Save
|
||||
#script.check=Check
|
||||
#script.format=Reformat
|
||||
|
||||
exclude_dialog.title=패키지 선택기
|
||||
exclude_dialog.ok=확인
|
||||
exclude_dialog.select_all=모두 선택
|
||||
|
||||
@@ -248,6 +248,11 @@ popup.search_global=Busca global "%s"
|
||||
#popup.add_scripts=Add scripts
|
||||
#popup.new_script=New script
|
||||
|
||||
#script.run=Run
|
||||
#script.save=Save
|
||||
#script.check=Check
|
||||
#script.format=Reformat
|
||||
|
||||
exclude_dialog.title=Selecionar pacote
|
||||
exclude_dialog.ok=OK
|
||||
exclude_dialog.select_all=Selecionar tudo
|
||||
|
||||
@@ -248,6 +248,11 @@ popup.search_global=全局搜索 “%s”
|
||||
#popup.add_scripts=Add scripts
|
||||
#popup.new_script=New script
|
||||
|
||||
#script.run=Run
|
||||
#script.save=Save
|
||||
#script.check=Check
|
||||
#script.format=Reformat
|
||||
|
||||
exclude_dialog.title=选择要排除的包
|
||||
exclude_dialog.ok=确定
|
||||
exclude_dialog.select_all=全选
|
||||
|
||||
@@ -248,6 +248,11 @@ popup.search_global=全域搜尋 "%s"
|
||||
#popup.add_scripts=Add scripts
|
||||
#popup.new_script=New script
|
||||
|
||||
#script.run=Run
|
||||
#script.save=Save
|
||||
#script.check=Check
|
||||
#script.format=Reformat
|
||||
|
||||
exclude_dialog.title=套件選擇
|
||||
exclude_dialog.ok=OK
|
||||
exclude_dialog.select_all=全選
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="#9AA7B0" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<rect width="12" height="2"/>
|
||||
<rect width="2" height="2" y="8"/>
|
||||
<rect width="9" height="2" y="4"/>
|
||||
<path d="M9.60330086,10.1966989 L9.60330086,3.19669886 L11.6033009,3.19669886 L11.6033009,10.1966989 L11.6033009,12.1966989 L5.60330086,12.1966989 L5.60330086,10.1966989 L9.60330086,10.1966989 Z" transform="rotate(45 8.603 7.697)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 515 B |
@@ -0,0 +1,4 @@
|
||||
<!-- 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="12" height="12" viewBox="0 0 12 12">
|
||||
<polygon fill="#59A869" fill-rule="evenodd" points="3 1 10 6 3 11"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 327 B |
@@ -0,0 +1,8 @@
|
||||
<!-- 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="13" height="13" viewBox="0 0 13 13">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<rect width="11" height="2" x="1" y="2" fill="#6E6E6E"/>
|
||||
<rect width="9" height="2" x="1" y="8" fill="#6E6E6E"/>
|
||||
<rect width="6" height="2" x="1" y="5" fill="#6E6E6E"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 482 B |
@@ -6,12 +6,18 @@ dependencies {
|
||||
|
||||
implementation("io.github.microutils:kotlin-logging-jvm:3.0.2")
|
||||
|
||||
// script context support in IDE is poor, use stubs and manual imports for now
|
||||
// kotlinScriptDef(project(":jadx-plugins:jadx-script:jadx-script-runtime"))
|
||||
|
||||
// manual imports (IDE can't import dependencies by scripts annotations)
|
||||
implementation("com.github.javafaker:javafaker:1.0.2")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs("scripts", "context")
|
||||
kotlin.srcDirs(
|
||||
"scripts",
|
||||
"context"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-2
@@ -3,11 +3,14 @@ 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.ReplAnalyzerResult
|
||||
import kotlin.script.experimental.api.ReplCompletionResult
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.api.SourceCodeCompletionVariant
|
||||
import kotlin.script.experimental.api.analysisDiagnostics
|
||||
import kotlin.script.experimental.api.renderedResultType
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
import kotlin.script.experimental.host.toScriptSource
|
||||
import kotlin.script.experimental.jvm.util.toSourceCodePosition
|
||||
@@ -19,14 +22,30 @@ data class ScriptCompletionResult(
|
||||
val reports: List<ScriptDiagnostic>
|
||||
)
|
||||
|
||||
class JadxScriptAutoComplete(private val scriptName: String) {
|
||||
data class ScriptAnalyzeResult(
|
||||
val errors: List<ScriptDiagnostic>,
|
||||
val renderType: String?,
|
||||
val reports: List<ScriptDiagnostic>
|
||||
)
|
||||
|
||||
class ScriptCompiler(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(),
|
||||
completions = result.valueOrNull()?.toList() ?: emptyList(),
|
||||
reports = result.reports
|
||||
)
|
||||
}
|
||||
|
||||
fun analyze(code: String, cursor: Int): ScriptAnalyzeResult {
|
||||
val result = analyze(code.toScriptSource(scriptName), cursor)
|
||||
val analyzerResult = result.valueOrNull()
|
||||
return ScriptAnalyzeResult(
|
||||
errors = analyzerResult?.get(ReplAnalyzerResult.analysisDiagnostics)?.toList() ?: emptyList(),
|
||||
renderType = analyzerResult?.get(ReplAnalyzerResult.renderedResultType),
|
||||
reports = result.reports
|
||||
)
|
||||
}
|
||||
@@ -36,4 +55,10 @@ class JadxScriptAutoComplete(private val scriptName: String) {
|
||||
replCompiler.complete(code, cursor.toSourceCodePosition(code), compileConf)
|
||||
}
|
||||
}
|
||||
|
||||
private fun analyze(code: SourceCode, cursor: Int): ResultWithDiagnostics<ReplAnalyzerResult> {
|
||||
return runBlocking {
|
||||
replCompiler.analyze(code, cursor.toSourceCodePosition(code), compileConf)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -2,8 +2,8 @@ package jadx.plugins.script.runner
|
||||
|
||||
import jadx.api.JadxDecompiler
|
||||
import jadx.api.plugins.JadxPluginContext
|
||||
import jadx.plugins.script.runtime.JadxScript
|
||||
import jadx.plugins.script.runtime.JadxScriptData
|
||||
import jadx.plugins.script.runtime.JadxScriptTemplate
|
||||
import jadx.plugins.script.runtime.data.JadxScriptAllOptions
|
||||
import mu.KotlinLogging
|
||||
import java.io.File
|
||||
@@ -43,10 +43,10 @@ class ScriptEval {
|
||||
processEvalResult(result, scriptFile)
|
||||
}
|
||||
|
||||
fun buildCompileConf() = createJvmCompilationConfigurationFromTemplate<JadxScript>()
|
||||
fun buildCompileConf() = createJvmCompilationConfigurationFromTemplate<JadxScriptTemplate>()
|
||||
|
||||
fun buildEvalConf(scriptData: JadxScriptData): ScriptEvaluationConfiguration {
|
||||
return createJvmEvaluationConfigurationFromTemplate<JadxScript> {
|
||||
return createJvmEvaluationConfigurationFromTemplate<JadxScriptTemplate> {
|
||||
constructorArgs(scriptData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ plugins {
|
||||
dependencies {
|
||||
api(project(":jadx-core"))
|
||||
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-common")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm")
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(kotlin("scripting-common"))
|
||||
implementation(kotlin("scripting-jvm"))
|
||||
|
||||
// allow to use maven dependencies in scripts
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies-maven")
|
||||
implementation(kotlin("scripting-dependencies"))
|
||||
implementation(kotlin("scripting-dependencies-maven"))
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
|
||||
implementation("io.github.microutils:kotlin-logging-jvm:3.0.2")
|
||||
|
||||
+20
-4
@@ -1,6 +1,7 @@
|
||||
package jadx.plugins.script.runtime
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mu.KotlinLogging
|
||||
import kotlin.script.experimental.annotations.KotlinScript
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.ScriptAcceptedLocation
|
||||
@@ -9,11 +10,11 @@ import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||
import kotlin.script.experimental.api.ScriptConfigurationRefinementContext
|
||||
import kotlin.script.experimental.api.acceptedLocations
|
||||
import kotlin.script.experimental.api.asSuccess
|
||||
import kotlin.script.experimental.api.baseClass
|
||||
import kotlin.script.experimental.api.collectedAnnotations
|
||||
import kotlin.script.experimental.api.defaultImports
|
||||
import kotlin.script.experimental.api.dependencies
|
||||
import kotlin.script.experimental.api.ide
|
||||
import kotlin.script.experimental.api.isStandalone
|
||||
import kotlin.script.experimental.api.onSuccess
|
||||
import kotlin.script.experimental.api.refineConfiguration
|
||||
import kotlin.script.experimental.api.with
|
||||
@@ -31,7 +32,22 @@ import kotlin.script.experimental.jvm.jvm
|
||||
fileExtension = "jadx.kts",
|
||||
compilationConfiguration = JadxScriptConfiguration::class
|
||||
)
|
||||
abstract class JadxScript
|
||||
abstract class JadxScriptTemplate(
|
||||
private val scriptData: JadxScriptData
|
||||
) {
|
||||
val scriptName = scriptData.scriptName
|
||||
val log = KotlinLogging.logger("JadxScript:$scriptName")
|
||||
|
||||
fun getJadxInstance() = JadxScriptInstance(scriptData, log)
|
||||
|
||||
fun println(message: Any?) {
|
||||
log.info(message?.toString())
|
||||
}
|
||||
|
||||
fun print(message: Any?) {
|
||||
log.info(message?.toString())
|
||||
}
|
||||
}
|
||||
|
||||
object JadxScriptConfiguration : ScriptCompilationConfiguration({
|
||||
defaultImports(DependsOn::class, Repository::class)
|
||||
@@ -45,11 +61,11 @@ object JadxScriptConfiguration : ScriptCompilationConfiguration({
|
||||
acceptedLocations(ScriptAcceptedLocation.Everywhere)
|
||||
}
|
||||
|
||||
baseClass(JadxScriptBaseClass::class)
|
||||
|
||||
refineConfiguration {
|
||||
onAnnotations(DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations)
|
||||
}
|
||||
|
||||
isStandalone(false)
|
||||
})
|
||||
|
||||
private val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), MavenDependenciesResolver())
|
||||
-16
@@ -17,24 +17,8 @@ import jadx.plugins.script.runtime.data.Replace
|
||||
import jadx.plugins.script.runtime.data.Search
|
||||
import jadx.plugins.script.runtime.data.Stages
|
||||
import mu.KLogger
|
||||
import mu.KotlinLogging
|
||||
import java.io.File
|
||||
|
||||
open class JadxScriptBaseClass(private val scriptData: JadxScriptData) {
|
||||
val scriptName = scriptData.scriptName
|
||||
val log = KotlinLogging.logger("JadxScript:$scriptName")
|
||||
|
||||
fun getJadxInstance() = JadxScriptInstance(scriptData, log)
|
||||
|
||||
fun println(message: Any?) {
|
||||
log.info(message?.toString())
|
||||
}
|
||||
|
||||
fun print(message: Any?) {
|
||||
log.info(message?.toString())
|
||||
}
|
||||
}
|
||||
|
||||
class JadxScriptData(
|
||||
val jadxInstance: JadxDecompiler,
|
||||
val pluginContext: JadxPluginContext,
|
||||
|
||||
-1
@@ -45,7 +45,6 @@ class JadxScriptOptions(
|
||||
private val jadx: JadxScriptInstance,
|
||||
private val options: JadxScriptAllOptions
|
||||
) {
|
||||
|
||||
fun <T> register(
|
||||
name: String,
|
||||
desc: String,
|
||||
|
||||
Reference in New Issue
Block a user