devtools: add shell window
This commit is contained in:
13
pom.xml
13
pom.xml
@@ -119,6 +119,7 @@
|
||||
<module>cache-updater</module>
|
||||
<module>runelite-api</module>
|
||||
<module>runelite-client</module>
|
||||
<module>runelite-jshell</module>
|
||||
<module>runelite-script-assembler-plugin</module>
|
||||
<module>http-api</module>
|
||||
<module>http-service</module>
|
||||
@@ -142,6 +143,11 @@
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-parent</artifactId>
|
||||
@@ -149,6 +155,13 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice-bom</artifactId>
|
||||
<version>4.1.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
<name>RuneLite Client</name>
|
||||
|
||||
<properties>
|
||||
<guice.version>4.1.0</guice.version>
|
||||
|
||||
<jarsigner.skip>true</jarsigner.skip>
|
||||
<pmd.skip>true</pmd.skip>
|
||||
</properties>
|
||||
@@ -85,7 +83,6 @@
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>${guice.version}</version>
|
||||
<classifier>no_aop</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -215,7 +212,6 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -223,6 +219,12 @@
|
||||
<artifactId>runelite-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.runelite</groupId>
|
||||
<artifactId>jshell</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.runelite</groupId>
|
||||
<artifactId>client-patch</artifactId>
|
||||
@@ -267,13 +269,11 @@
|
||||
<dependency>
|
||||
<groupId>com.google.inject.extensions</groupId>
|
||||
<artifactId>guice-testlib</artifactId>
|
||||
<version>${guice.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject.extensions</groupId>
|
||||
<artifactId>guice-grapher</artifactId>
|
||||
<version>${guice.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -318,7 +318,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
|
||||
@@ -51,26 +51,20 @@ import net.runelite.client.util.ReflectUtil;
|
||||
@ThreadSafe
|
||||
public class EventBus
|
||||
{
|
||||
@FunctionalInterface
|
||||
public interface SubscriberMethod
|
||||
{
|
||||
void invoke(Object event);
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class Subscriber
|
||||
public static class Subscriber
|
||||
{
|
||||
private final Object object;
|
||||
private final Method method;
|
||||
private final float priority;
|
||||
@EqualsAndHashCode.Exclude
|
||||
private final SubscriberMethod lamda;
|
||||
private final Consumer<Object> lambda;
|
||||
|
||||
void invoke(final Object arg) throws Exception
|
||||
{
|
||||
if (lamda != null)
|
||||
if (lambda != null)
|
||||
{
|
||||
lamda.invoke(arg);
|
||||
lambda.accept(arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -80,7 +74,9 @@ public class EventBus
|
||||
}
|
||||
|
||||
private final Consumer<Throwable> exceptionHandler;
|
||||
private ImmutableMultimap<Class, Subscriber> subscribers = ImmutableMultimap.of();
|
||||
|
||||
@Nonnull
|
||||
private ImmutableMultimap<Class<?>, Subscriber> subscribers = ImmutableMultimap.of();
|
||||
|
||||
/**
|
||||
* Instantiates EventBus with default exception handler
|
||||
@@ -99,13 +95,8 @@ public class EventBus
|
||||
*/
|
||||
public synchronized void register(@Nonnull final Object object)
|
||||
{
|
||||
final ImmutableMultimap.Builder<Class, Subscriber> builder = ImmutableMultimap.builder();
|
||||
|
||||
if (subscribers != null)
|
||||
{
|
||||
builder.putAll(subscribers);
|
||||
}
|
||||
|
||||
final ImmutableMultimap.Builder<Class<?>, Subscriber> builder = ImmutableMultimap.builder();
|
||||
builder.putAll(subscribers);
|
||||
builder.orderValuesBy(Comparator.comparing(Subscriber::getPriority).reversed()
|
||||
.thenComparing(s -> s.object.getClass().getName()));
|
||||
|
||||
@@ -141,7 +132,7 @@ public class EventBus
|
||||
Preconditions.checkArgument(method.getName().equals(preferredName), "Subscribed method " + method + " should be named " + preferredName);
|
||||
|
||||
method.setAccessible(true);
|
||||
SubscriberMethod lambda = null;
|
||||
Consumer<Object> lambda = null;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -150,14 +141,14 @@ public class EventBus
|
||||
final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription);
|
||||
final CallSite site = LambdaMetafactory.metafactory(
|
||||
caller,
|
||||
"invoke",
|
||||
MethodType.methodType(SubscriberMethod.class, clazz),
|
||||
"accept",
|
||||
MethodType.methodType(Consumer.class, clazz),
|
||||
subscription.changeParameterType(0, Object.class),
|
||||
target,
|
||||
subscription);
|
||||
|
||||
final MethodHandle factory = site.getTarget();
|
||||
lambda = (SubscriberMethod) factory.bindTo(object).invokeExact();
|
||||
lambda = (Consumer<Object>) factory.bindTo(object).invokeExact();
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
@@ -173,6 +164,21 @@ public class EventBus
|
||||
subscribers = builder.build();
|
||||
}
|
||||
|
||||
public synchronized <T> Subscriber register(Class<T> clazz, Consumer<T> subFn, float priority)
|
||||
{
|
||||
final ImmutableMultimap.Builder<Class<?>, Subscriber> builder = ImmutableMultimap.builder();
|
||||
builder.putAll(subscribers);
|
||||
builder.orderValuesBy(Comparator.comparing(Subscriber::getPriority).reversed()
|
||||
.thenComparing(s -> s.object.getClass().getName()));
|
||||
|
||||
Subscriber sub = new Subscriber(subFn, null, priority, (Consumer<Object>) subFn);
|
||||
builder.put(clazz, sub);
|
||||
|
||||
subscribers = builder.build();
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters all subscribed methods from provided subscriber object.
|
||||
*
|
||||
@@ -180,12 +186,7 @@ public class EventBus
|
||||
*/
|
||||
public synchronized void unregister(@Nonnull final Object object)
|
||||
{
|
||||
if (subscribers == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Multimap<Class, Subscriber> map = HashMultimap.create();
|
||||
final Multimap<Class<?>, Subscriber> map = HashMultimap.create();
|
||||
map.putAll(subscribers);
|
||||
|
||||
for (Class<?> clazz = object.getClass(); clazz != null; clazz = clazz.getSuperclass())
|
||||
@@ -207,6 +208,21 @@ public class EventBus
|
||||
subscribers = ImmutableMultimap.copyOf(map);
|
||||
}
|
||||
|
||||
public synchronized void unregister(Subscriber sub)
|
||||
{
|
||||
if (sub == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Multimap<Class<?>, Subscriber> map = HashMultimap.create();
|
||||
map.putAll(subscribers);
|
||||
|
||||
map.values().remove(sub);
|
||||
|
||||
subscribers = ImmutableMultimap.copyOf(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts provided event to all registered subscribers. Subscriber calls are invoked immediately,
|
||||
* ordered by priority then their declaring class' name.
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
package net.runelite.client.plugins.devtools;
|
||||
|
||||
import com.google.inject.ProvisionException;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.TrayIcon;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.MenuAction;
|
||||
@@ -44,6 +46,7 @@ import net.runelite.client.ui.overlay.infobox.Counter;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
@Slf4j
|
||||
class DevToolsPanel extends PluginPanel
|
||||
{
|
||||
private final Client client;
|
||||
@@ -174,6 +177,21 @@ class DevToolsPanel extends PluginPanel
|
||||
disconnectBtn.addActionListener(e -> clientThread.invoke(() -> client.setGameState(GameState.CONNECTION_LOST)));
|
||||
container.add(disconnectBtn);
|
||||
|
||||
try
|
||||
{
|
||||
ShellFrame sf = plugin.getInjector().getInstance(ShellFrame.class);
|
||||
container.add(plugin.getShell());
|
||||
plugin.getShell().addFrame(sf);
|
||||
}
|
||||
catch (LinkageError | ProvisionException e)
|
||||
{
|
||||
log.debug("Shell is not supported", e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.info("Shell couldn't be loaded", e);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ public class DevToolsPlugin extends Plugin
|
||||
private DevToolsButton soundEffects;
|
||||
private DevToolsButton scriptInspector;
|
||||
private DevToolsButton inventoryInspector;
|
||||
private DevToolsButton shell;
|
||||
private NavigationButton navButton;
|
||||
|
||||
@Provides
|
||||
@@ -187,6 +188,7 @@ public class DevToolsPlugin extends Plugin
|
||||
soundEffects = new DevToolsButton("Sound Effects");
|
||||
scriptInspector = new DevToolsButton("Script Inspector");
|
||||
inventoryInspector = new DevToolsButton("Inventory Inspector");
|
||||
shell = new DevToolsButton("Shell");
|
||||
|
||||
overlayManager.add(overlay);
|
||||
overlayManager.add(locationOverlay);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.devtools;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.client.RuneLite;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.jshell.ShellPanel;
|
||||
|
||||
@Singleton
|
||||
class ShellFrame extends DevToolsFrame
|
||||
{
|
||||
private final ShellPanel shellPanel;
|
||||
|
||||
@Inject
|
||||
ShellFrame(ClientThread clientThread, ScheduledExecutorService executor)
|
||||
{
|
||||
this.shellPanel = new ShellPanel(executor)
|
||||
{
|
||||
@Override
|
||||
protected void invokeOnClientThread(Runnable r)
|
||||
{
|
||||
clientThread.invoke(r);
|
||||
}
|
||||
};
|
||||
setContentPane(shellPanel);
|
||||
|
||||
setTitle("RuneLite Shell");
|
||||
|
||||
pack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open()
|
||||
{
|
||||
shellPanel.switchContext(RuneLite.getInjector());
|
||||
super.open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
super.close();
|
||||
shellPanel.freeContext();
|
||||
}
|
||||
}
|
||||
83
runelite-jshell/pom.xml
Normal file
83
runelite-jshell/pom.xml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>net.runelite</groupId>
|
||||
<artifactId>runelite-parent</artifactId>
|
||||
<version>1.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>jshell</artifactId>
|
||||
<name>RuneLite JShell</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<classifier>no_aop</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fifesoft</groupId>
|
||||
<artifactId>rsyntaxtextarea</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fifesoft</groupId>
|
||||
<artifactId>autocomplete</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
<release>11</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.jshell;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import jdk.jshell.JShell;
|
||||
import jdk.jshell.SourceCodeAnalysis;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.fife.ui.autocomplete.BasicCompletion;
|
||||
import org.fife.ui.autocomplete.Completion;
|
||||
import org.fife.ui.autocomplete.CompletionProviderBase;
|
||||
import org.fife.ui.autocomplete.ParameterizedCompletion;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class JShellAutocompleteProvider extends CompletionProviderBase
|
||||
{
|
||||
private final JShell shell;
|
||||
private String anchorText;
|
||||
private List<Completion> completions;
|
||||
|
||||
@Override
|
||||
protected List<Completion> getCompletionsImpl(JTextComponent comp)
|
||||
{
|
||||
return completions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlreadyEnteredText(JTextComponent comp)
|
||||
{
|
||||
complete(comp);
|
||||
return anchorText;
|
||||
}
|
||||
|
||||
private void complete(JTextComponent comp)
|
||||
{
|
||||
completions = Collections.emptyList();
|
||||
|
||||
String src = comp.getText();
|
||||
int cursor = comp.getCaretPosition();
|
||||
|
||||
for (int offset = 0; offset < src.length() && cursor >= offset; )
|
||||
{
|
||||
var snipSrc = src.substring(offset);
|
||||
int thisOffset = offset;
|
||||
var ci = shell.sourceCodeAnalysis().analyzeCompletion(snipSrc);
|
||||
offset = src.length() - ci.remaining().length();
|
||||
boolean mayHaveMore = ci.completeness() == SourceCodeAnalysis.Completeness.COMPLETE_WITH_SEMI
|
||||
|| ci.completeness() == SourceCodeAnalysis.Completeness.COMPLETE;
|
||||
|
||||
if (cursor <= offset || !mayHaveMore)
|
||||
{
|
||||
var anchor = new int[1];
|
||||
|
||||
completions = shell.sourceCodeAnalysis()
|
||||
.completionSuggestions(snipSrc, cursor - thisOffset, anchor)
|
||||
.stream()
|
||||
.filter(v -> !v.continuation().startsWith("$"))
|
||||
.map(s ->
|
||||
{
|
||||
return new BasicCompletion(this, s.continuation());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
anchorText = snipSrc.substring(anchor[0], cursor - thisOffset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (completions.isEmpty())
|
||||
{
|
||||
anchorText = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Completion> getCompletionsAt(JTextComponent comp, Point p)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoActivateOkay(JTextComponent comp)
|
||||
{
|
||||
// try not to start autocomplete when it has no useful context
|
||||
String text = comp.getText();
|
||||
for (int i = comp.getCaretPosition(); i >= 0; i--)
|
||||
{
|
||||
char c = text.charAt(i);
|
||||
if (Character.isJavaIdentifierPart(c) || c == '.' || c == '(')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (Character.isWhitespace(c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ParameterizedCompletion> getParameterizedCompletions(JTextComponent tc)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.jshell;
|
||||
|
||||
import java.util.Map;
|
||||
import jdk.jshell.execution.DirectExecutionControl;
|
||||
import jdk.jshell.spi.ExecutionControl;
|
||||
import jdk.jshell.spi.ExecutionControlProvider;
|
||||
import jdk.jshell.spi.ExecutionEnv;
|
||||
|
||||
public class RLShellExecutionControl extends DirectExecutionControl implements ExecutionControlProvider
|
||||
{
|
||||
public RLShellExecutionControl()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) throws Throwable
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.jshell;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jshell.EvalException;
|
||||
|
||||
class RemappingThrowable extends Throwable
|
||||
{
|
||||
private final String source;
|
||||
private final Map<String, Integer> offsets;
|
||||
private final Throwable wrapped;
|
||||
private final Map<Throwable, Throwable> dejaVu;
|
||||
|
||||
public RemappingThrowable(String source, Map<String, Integer> offsets, Throwable other)
|
||||
{
|
||||
this(source, offsets, other, new HashMap<>());
|
||||
}
|
||||
|
||||
private RemappingThrowable(String source, Map<String, Integer> offsets, Throwable other, Map<Throwable, Throwable> dejaVu)
|
||||
{
|
||||
super();
|
||||
|
||||
this.source = source;
|
||||
this.offsets = offsets;
|
||||
this.wrapped = other;
|
||||
this.dejaVu = dejaVu;
|
||||
|
||||
dejaVu.put(wrapped, this);
|
||||
|
||||
setStackTrace(Stream.of(wrapped.getStackTrace())
|
||||
.map(e ->
|
||||
{
|
||||
Integer boxOffset = offsets.get(e.getFileName());
|
||||
if (boxOffset == null)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
int offset = boxOffset;
|
||||
int line = e.getLineNumber();
|
||||
for (int i = 0; i <= offset && i < source.length(); i++)
|
||||
{
|
||||
if (source.charAt(i) == '\n')
|
||||
{
|
||||
line++;
|
||||
}
|
||||
}
|
||||
return new StackTraceElement(
|
||||
Strings.isNullOrEmpty(e.getClassName()) ? "Shell" : e.getClassName(),
|
||||
Strings.isNullOrEmpty(e.getMethodName()) ? "global" : e.getMethodName(),
|
||||
"",
|
||||
line);
|
||||
})
|
||||
.toArray(StackTraceElement[]::new));
|
||||
|
||||
if (wrapped.getCause() != null)
|
||||
{
|
||||
initCause(remap(wrapped.getCause()));
|
||||
}
|
||||
|
||||
for (Throwable suppressed : wrapped.getSuppressed())
|
||||
{
|
||||
addSuppressed(remap(suppressed));
|
||||
}
|
||||
}
|
||||
|
||||
private Throwable remap(Throwable other)
|
||||
{
|
||||
Throwable remap = dejaVu.get(other);
|
||||
if (remap == null)
|
||||
{
|
||||
remap = new RemappingThrowable(source, offsets, other, dejaVu);
|
||||
// ctor inserts into the map
|
||||
}
|
||||
return remap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
return wrapped.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalizedMessage()
|
||||
{
|
||||
return wrapped.getLocalizedMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String className;
|
||||
if (wrapped instanceof EvalException)
|
||||
{
|
||||
className = ((EvalException) wrapped).getExceptionClassName();
|
||||
}
|
||||
else
|
||||
{
|
||||
className = wrapped.getClass().getName();
|
||||
}
|
||||
|
||||
String message = wrapped.getLocalizedMessage();
|
||||
if (message == null)
|
||||
{
|
||||
return className;
|
||||
}
|
||||
return className + ": " + message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.jshell;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Injector;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Segment;
|
||||
import jdk.jshell.Diag;
|
||||
import jdk.jshell.JShell;
|
||||
import jdk.jshell.Snippet;
|
||||
import jdk.jshell.SnippetEvent;
|
||||
import jdk.jshell.SourceCodeAnalysis;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.fife.ui.autocomplete.AutoCompletion;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
import org.fife.ui.rsyntaxtextarea.Theme;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Slf4j
|
||||
public abstract class ShellPanel extends JPanel
|
||||
{
|
||||
private final ScheduledExecutorService executor;
|
||||
|
||||
private final RSyntaxTextArea textArea;
|
||||
private final JTextArea console = new JTextArea();
|
||||
|
||||
@Getter
|
||||
private final Logger shellLogger;
|
||||
|
||||
private final List<Runnable> cleanup = new ArrayList<>();
|
||||
|
||||
private RLShellExecutionControl exec;
|
||||
private JShell shell;
|
||||
private Set<Snippet> prelude;
|
||||
private Injector injector;
|
||||
private AutoCompletion autoCompletion;
|
||||
|
||||
public static ShellPanel INSTANCE;
|
||||
|
||||
public ShellPanel(ScheduledExecutorService executor)
|
||||
{
|
||||
this.executor = executor;
|
||||
|
||||
Font codeFont = Stream.of(
|
||||
"Source code pro",
|
||||
"DejaVu Sans Code",
|
||||
"Consolas",
|
||||
Font.MONOSPACED)
|
||||
.map(name -> new Font(name, Font.PLAIN, 12))
|
||||
.filter(f -> !"Dialog.plain".equals(f.getFontName()))
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
JPanel topPanel = new JPanel();
|
||||
topPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
|
||||
|
||||
JButton run = new JButton("⯈");
|
||||
run.setToolTipText("Run");
|
||||
run.addActionListener(ev -> run());
|
||||
topPanel.add(run);
|
||||
|
||||
JButton clear = new JButton("🗑");
|
||||
run.setToolTipText("Clear console");
|
||||
clear.addActionListener(ev -> console.setText(""));
|
||||
topPanel.add(clear);
|
||||
|
||||
add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
textArea = new RSyntaxTextArea();
|
||||
|
||||
try
|
||||
{
|
||||
// RSyntaxTextArea::setAntiAliasingEnabled actually forces it to match the platform's
|
||||
// default, which is pointless
|
||||
var map = new HashMap<RenderingHints.Key, Object>();
|
||||
map.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
var f = RSyntaxTextArea.class.getDeclaredField("aaHints");
|
||||
f.setAccessible(true);
|
||||
f.set(textArea, map);
|
||||
}
|
||||
catch (ReflectiveOperationException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
textArea.setFont(codeFont);
|
||||
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
||||
textArea.setAutoIndentEnabled(true);
|
||||
textArea.setPaintTabLines(true);
|
||||
textArea.setShowMatchedBracketPopup(true);
|
||||
textArea.setCloseCurlyBraces(false);
|
||||
textArea.setTabSize(2);
|
||||
textArea.setMarkOccurrences(true);
|
||||
textArea.setMarkOccurrencesDelay(200);
|
||||
textArea.addKeyListener(new KeyAdapter()
|
||||
{
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
if (e.getKeyCode() == KeyEvent.VK_R && (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0)
|
||||
{
|
||||
run();
|
||||
e.consume();
|
||||
}
|
||||
if (e.getKeyCode() == KeyEvent.VK_F10)
|
||||
{
|
||||
run();
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var textScrollArea = new RTextScrollPane(textArea);
|
||||
|
||||
try
|
||||
{
|
||||
Theme.load(ShellPanel.class.getResourceAsStream("darcula.xml"), codeFont)
|
||||
.apply(textArea);
|
||||
|
||||
try (var is = ShellPanel.class.getResourceAsStream("default.jsh"))
|
||||
{
|
||||
textArea.setText(new String(is.readAllBytes(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
console.setFont(codeFont);
|
||||
console.setFocusable(false);
|
||||
console.setEditable(false);
|
||||
console.setOpaque(false); // this turns off the hover effect for some reason
|
||||
|
||||
var split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textScrollArea, new JScrollPane(console));
|
||||
split.setResizeWeight(.8);
|
||||
split.setPreferredSize(new Dimension(800, 800));
|
||||
add(split, BorderLayout.CENTER);
|
||||
|
||||
shellLogger = new TeeLogger(LoggerFactory.getLogger("Shell"), this::logToConsole);
|
||||
INSTANCE = this;
|
||||
|
||||
// make sure jshell is on the classpath
|
||||
JShell.builder();
|
||||
}
|
||||
|
||||
public void switchContext(Injector injector)
|
||||
{
|
||||
freeContext();
|
||||
|
||||
this.injector = injector;
|
||||
|
||||
exec = new RLShellExecutionControl()
|
||||
{
|
||||
@Override
|
||||
protected String invoke(Method doitMethod) throws Exception
|
||||
{
|
||||
var result = new AtomicReference<>();
|
||||
var sema = new Semaphore(0);
|
||||
invokeOnClientThread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
result.set(super.invoke(doitMethod));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.set(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
sema.release();
|
||||
}
|
||||
});
|
||||
sema.acquire();
|
||||
if (result.get() instanceof String)
|
||||
{
|
||||
return (String) result.get();
|
||||
}
|
||||
throw (Exception) result.get();
|
||||
}
|
||||
};
|
||||
|
||||
shell = JShell.builder()
|
||||
.executionEngine(exec, null)
|
||||
.build();
|
||||
|
||||
String preludeStr;
|
||||
try (var is = ShellPanel.class.getResourceAsStream("prelude.jsh"))
|
||||
{
|
||||
preludeStr = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
prelude = ImmutableSet.copyOf(eval(preludeStr, false));
|
||||
|
||||
var cp = new JShellAutocompleteProvider(shell);
|
||||
autoCompletion = new AutoCompletion(cp);
|
||||
autoCompletion.setAutoActivationDelay(200);
|
||||
autoCompletion.setAutoActivationEnabled(true);
|
||||
autoCompletion.setAutoCompleteSingleChoices(false);
|
||||
autoCompletion.install(this.textArea);
|
||||
}
|
||||
|
||||
public void logToConsole(String message)
|
||||
{
|
||||
SwingUtilities.invokeLater(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = console.getDocument();
|
||||
if (doc.getLength() > 100_000)
|
||||
{
|
||||
Segment seg = new Segment();
|
||||
int i = doc.getLength() - 75_000;
|
||||
for (; i < doc.getLength(); i++)
|
||||
{
|
||||
doc.getText(i, 1, seg);
|
||||
if (seg.array[0] == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
doc.remove(0, i);
|
||||
}
|
||||
doc.insertString(doc.getLength(), message + "\n", null);
|
||||
console.setCaretPosition(doc.getLength());
|
||||
}
|
||||
catch (BadLocationException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<Snippet> eval(String src, boolean isUserCode)
|
||||
{
|
||||
var out = new ArrayList<Snippet>();
|
||||
var offsets = new HashMap<String, Integer>();
|
||||
String output = null;
|
||||
evaluation:
|
||||
for (int offset = 0; offset < src.length(); )
|
||||
{
|
||||
// Workaround a jdk bug
|
||||
for (; src.charAt(offset) == '\n'; offset++);
|
||||
|
||||
var ci = shell.sourceCodeAnalysis().analyzeCompletion(src.substring(offset));
|
||||
int thisOffset = offset;
|
||||
offset = src.length() - ci.remaining().length();
|
||||
if (ci.completeness() == SourceCodeAnalysis.Completeness.EMPTY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
List<SnippetEvent> evs = shell.eval(ci.source());
|
||||
for (var ev : evs)
|
||||
{
|
||||
Snippet snip = ev.snippet();
|
||||
offsets.put("#" + snip.id(), thisOffset);
|
||||
if (ev.status() != Snippet.Status.VALID && ev.status() != Snippet.Status.RECOVERABLE_DEFINED)
|
||||
{
|
||||
var diags = shell.diagnostics(snip).collect(Collectors.toList());
|
||||
for (var diag : diags)
|
||||
{
|
||||
String msg = toStringDiagnostic(src, thisOffset, diag);
|
||||
if (isUserCode)
|
||||
{
|
||||
logToConsole(msg);
|
||||
// It might be nice to highlight stuff here
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("prelude error: " + msg);
|
||||
}
|
||||
}
|
||||
if (diags.isEmpty())
|
||||
{
|
||||
logToConsole("bad snippet" + ev.status());
|
||||
}
|
||||
break evaluation;
|
||||
}
|
||||
if (ev.exception() != null)
|
||||
{
|
||||
if (isUserCode)
|
||||
{
|
||||
shellLogger.error("", new RemappingThrowable(src, offsets, ev.exception()));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("prelude error", ev.exception());
|
||||
}
|
||||
}
|
||||
output = ev.value();
|
||||
|
||||
out.add(snip);
|
||||
}
|
||||
}
|
||||
|
||||
if (isUserCode && !Strings.isNullOrEmpty(output))
|
||||
{
|
||||
logToConsole("[OUTPUT] " + output);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private String toStringDiagnostic(String source, int offset, Diag diag)
|
||||
{
|
||||
int line = 1;
|
||||
int column = 1;
|
||||
offset += (int) diag.getPosition();
|
||||
for (int i = 0; i < offset && i < source.length(); i++)
|
||||
{
|
||||
if (source.charAt(i) == '\n')
|
||||
{
|
||||
line++;
|
||||
column = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
column++;
|
||||
}
|
||||
}
|
||||
|
||||
return line + ":" + column + ": " + diag.getMessage(Locale.getDefault());
|
||||
}
|
||||
|
||||
protected void run()
|
||||
{
|
||||
String text = textArea.getText();
|
||||
executor.submit(() ->
|
||||
{
|
||||
shell.snippets()
|
||||
.filter(v -> !prelude.contains(v))
|
||||
.forEach(shell::drop);
|
||||
|
||||
cleanup();
|
||||
|
||||
eval(text, true);
|
||||
});
|
||||
}
|
||||
|
||||
public void freeContext()
|
||||
{
|
||||
cleanup();
|
||||
|
||||
exec = null;
|
||||
shell = null;
|
||||
prelude = null;
|
||||
injector = null;
|
||||
|
||||
if (autoCompletion != null)
|
||||
{
|
||||
autoCompletion.uninstall();
|
||||
}
|
||||
autoCompletion = null;
|
||||
|
||||
console.setText("");
|
||||
}
|
||||
|
||||
private void cleanup()
|
||||
{
|
||||
for (var c : cleanup)
|
||||
{
|
||||
try
|
||||
{
|
||||
c.run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
shellLogger.error("Cleanup threw:", e);
|
||||
}
|
||||
}
|
||||
cleanup.clear();
|
||||
}
|
||||
|
||||
protected abstract void invokeOnClientThread(Runnable r);
|
||||
|
||||
public <T> T inject(Class<T> clazz)
|
||||
{
|
||||
return injector.getInstance(clazz);
|
||||
}
|
||||
|
||||
public void cleanup(Runnable r)
|
||||
{
|
||||
cleanup.add(r);
|
||||
}
|
||||
}
|
||||
483
runelite-jshell/src/main/java/net/runelite/jshell/TeeLogger.java
Normal file
483
runelite-jshell/src/main/java/net/runelite/jshell/TeeLogger.java
Normal file
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.jshell;
|
||||
|
||||
import java.io.CharArrayWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.helpers.FormattingTuple;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
@SuppressWarnings("PlaceholderCountMatchesArgumentCount")
|
||||
@RequiredArgsConstructor
|
||||
public class TeeLogger implements Logger
|
||||
{
|
||||
private static final String TRACE = "[TRACE] ";
|
||||
private static final String DEBUG = "[DEBUG] ";
|
||||
private static final String INFO = "[INFO] ";
|
||||
private static final String WARN = "[WARN] ";
|
||||
private static final String ERROR = "[ERROR] ";
|
||||
|
||||
private final Logger delegate;
|
||||
private final Consumer<String> messageConsumer;
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "RLShell";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private void log(String level, String message, Object... format)
|
||||
{
|
||||
FormattingTuple fmt = MessageFormatter.arrayFormat(message, format);
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append(level).append(fmt.getMessage());
|
||||
Throwable throwable = fmt.getThrowable();
|
||||
if (throwable != null)
|
||||
{
|
||||
msg.append("\n");
|
||||
var caw = new CharArrayWriter();
|
||||
try (PrintWriter pw = new PrintWriter(caw))
|
||||
{
|
||||
throwable.printStackTrace(pw);
|
||||
}
|
||||
msg.append(caw.toString());
|
||||
}
|
||||
messageConsumer.accept(msg.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg)
|
||||
{
|
||||
delegate.trace(msg);
|
||||
log(TRACE, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object arg)
|
||||
{
|
||||
delegate.trace(format, arg);
|
||||
log(TRACE, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.trace(format, arg1, arg2);
|
||||
log(TRACE, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object... arguments)
|
||||
{
|
||||
delegate.trace(format, arguments);
|
||||
log(TRACE, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg, Throwable t)
|
||||
{
|
||||
delegate.trace(msg, t);
|
||||
log(TRACE, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled(Marker marker)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String msg)
|
||||
{
|
||||
delegate.trace(marker, msg);
|
||||
log(TRACE, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String format, Object arg)
|
||||
{
|
||||
delegate.trace(marker, format, arg);
|
||||
log(TRACE, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.trace(marker, format, arg1, arg2);
|
||||
log(TRACE, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String format, Object... argArray)
|
||||
{
|
||||
delegate.trace(marker, format, argArray);
|
||||
log(TRACE, format, argArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String msg, Throwable t)
|
||||
{
|
||||
delegate.trace(marker, msg, t);
|
||||
log(TRACE, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg)
|
||||
{
|
||||
delegate.debug(msg);
|
||||
log(DEBUG, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object arg)
|
||||
{
|
||||
delegate.debug(format, arg);
|
||||
log(DEBUG, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.debug(format, arg1, arg2);
|
||||
log(DEBUG, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object... arguments)
|
||||
{
|
||||
delegate.debug(format, arguments);
|
||||
log(DEBUG, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg, Throwable t)
|
||||
{
|
||||
delegate.debug(msg, t);
|
||||
log(DEBUG, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled(Marker marker)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String msg)
|
||||
{
|
||||
delegate.debug(marker, msg);
|
||||
log(DEBUG, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String format, Object arg)
|
||||
{
|
||||
delegate.debug(marker, format, arg);
|
||||
log(DEBUG, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.debug(marker, format, arg1, arg2);
|
||||
log(DEBUG, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String format, Object... arguments)
|
||||
{
|
||||
delegate.debug(marker, format, arguments);
|
||||
log(DEBUG, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String msg, Throwable t)
|
||||
{
|
||||
delegate.debug(marker, msg, t);
|
||||
log(DEBUG, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String msg)
|
||||
{
|
||||
delegate.info(msg);
|
||||
log(INFO, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object arg)
|
||||
{
|
||||
delegate.info(format, arg);
|
||||
log(INFO, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.info(format, arg1, arg2);
|
||||
log(INFO, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object... arguments)
|
||||
{
|
||||
delegate.info(format, arguments);
|
||||
log(INFO, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String msg, Throwable t)
|
||||
{
|
||||
delegate.info(msg, t);
|
||||
log(INFO, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled(Marker marker)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String msg)
|
||||
{
|
||||
delegate.info(marker, msg);
|
||||
log(INFO, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String format, Object arg)
|
||||
{
|
||||
delegate.info(marker, format, arg);
|
||||
log(INFO, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.info(marker, format, arg1, arg2);
|
||||
log(INFO, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String format, Object... arguments)
|
||||
{
|
||||
delegate.info(marker, format, arguments);
|
||||
log(INFO, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String msg, Throwable t)
|
||||
{
|
||||
delegate.info(marker, msg, t);
|
||||
log(INFO, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg)
|
||||
{
|
||||
delegate.warn(msg);
|
||||
log(WARN, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object arg)
|
||||
{
|
||||
delegate.warn(format, arg);
|
||||
log(WARN, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object... arguments)
|
||||
{
|
||||
delegate.warn(format, arguments);
|
||||
log(WARN, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.warn(format, arg1, arg2);
|
||||
log(WARN, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable t)
|
||||
{
|
||||
delegate.warn(msg, t);
|
||||
log(WARN, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled(Marker marker)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String msg)
|
||||
{
|
||||
delegate.warn(marker, msg);
|
||||
log(WARN, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String format, Object arg)
|
||||
{
|
||||
delegate.warn(marker, format, arg);
|
||||
log(WARN, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.warn(marker, format, arg1, arg2);
|
||||
log(WARN, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String format, Object... arguments)
|
||||
{
|
||||
delegate.warn(marker, format, arguments);
|
||||
log(WARN, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String msg, Throwable t)
|
||||
{
|
||||
delegate.warn(marker, msg, t);
|
||||
log(WARN, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String msg)
|
||||
{
|
||||
delegate.error(msg);
|
||||
log(ERROR, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object arg)
|
||||
{
|
||||
delegate.error(format, arg);
|
||||
log(ERROR, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.error(format, arg1, arg2);
|
||||
log(ERROR, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object... arguments)
|
||||
{
|
||||
delegate.error(format, arguments);
|
||||
log(ERROR, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String msg, Throwable t)
|
||||
{
|
||||
delegate.error(msg, t);
|
||||
log(ERROR, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled(Marker marker)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String msg)
|
||||
{
|
||||
delegate.error(marker, msg);
|
||||
log(ERROR, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String format, Object arg)
|
||||
{
|
||||
delegate.error(marker, format, arg);
|
||||
log(ERROR, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String format, Object arg1, Object arg2)
|
||||
{
|
||||
delegate.error(marker, format, arg1, arg2);
|
||||
log(ERROR, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String format, Object... arguments)
|
||||
{
|
||||
delegate.error(marker, format, arguments);
|
||||
log(ERROR, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String msg, Throwable t)
|
||||
{
|
||||
delegate.error(marker, msg, t);
|
||||
log(ERROR, msg, t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE RSyntaxTheme SYSTEM "theme.dtd">
|
||||
|
||||
<RSyntaxTheme version="1.0">
|
||||
<!-- General editor colors. -->
|
||||
<background color="2B2B2B"/>
|
||||
<caret color="BBBBBB"/>
|
||||
<selection useFG="false" bg="214283" roundedEdges="false"/>
|
||||
<currentLineHighlight color="323232" fade="false"/>
|
||||
<marginLine fg="394448"/>
|
||||
<markAllHighlight color="155221"/>
|
||||
<markOccurrencesHighlight color="32593D" border="true"/>
|
||||
<matchedBracket fg="A9B7C6" bg="2E2E2E" highlightBoth="true" animate="true"/><!--ij draws a border-->
|
||||
<hyperlinks fg="287BDE"/>
|
||||
<secondaryLanguages>
|
||||
<language index="1" bg="333344"/>
|
||||
<language index="2" bg="223322"/>
|
||||
<language index="3" bg="332222"/>
|
||||
</secondaryLanguages>
|
||||
|
||||
<!-- Gutter styling. -->
|
||||
<gutterBorder color="606366"/>
|
||||
<lineNumbers fg="606366"/>
|
||||
<foldIndicator fg="6A8088" iconBg="2f383c" iconArmedBg="3f484c"/>
|
||||
<iconRowHeader activeLineRange="878787"/>
|
||||
|
||||
<!-- Syntax tokens. -->
|
||||
<tokenStyles>
|
||||
<style token="IDENTIFIER" fg="A9B7C6"/>
|
||||
<style token="RESERVED_WORD" fg="CC7832" bold="false"/>
|
||||
<style token="RESERVED_WORD_2" fg="CC7832" bold="false"/>
|
||||
<style token="ANNOTATION" fg="BBB529"/>
|
||||
<style token="COMMENT_DOCUMENTATION" fg="629755"/>
|
||||
<style token="COMMENT_EOL" fg="808080"/>
|
||||
<style token="COMMENT_MULTILINE" fg="808080"/>
|
||||
<style token="COMMENT_KEYWORD" fg="629755"/>
|
||||
<style token="COMMENT_MARKUP" fg="77B767"/>
|
||||
<style token="FUNCTION" fg="A9B7C6"/><!-- any identifier magically known -->
|
||||
<style token="DATA_TYPE" fg="CC7832" bold="false"/>
|
||||
<style token="LITERAL_BOOLEAN" fg="CC7832" bold="false"/>
|
||||
<style token="LITERAL_NUMBER_DECIMAL_INT" fg="6897BB"/>
|
||||
<style token="LITERAL_NUMBER_FLOAT" fg="6897BB"/>
|
||||
<style token="LITERAL_NUMBER_HEXADECIMAL" fg="6897BB"/>
|
||||
<style token="LITERAL_STRING_DOUBLE_QUOTE" fg="6A8759"/>
|
||||
<style token="LITERAL_CHAR" fg="6A8759"/>
|
||||
<style token="LITERAL_BACKQUOTE" fg="6A8759"/>
|
||||
|
||||
<!-- all wrong but nobody will write xml in this -->
|
||||
<style token="MARKUP_TAG_DELIMITER" fg="F92672"/>
|
||||
<style token="MARKUP_TAG_NAME" fg="ABBFD3" bold="true"/>
|
||||
<style token="MARKUP_TAG_ATTRIBUTE" fg="B3B689"/>
|
||||
<style token="MARKUP_TAG_ATTRIBUTE_VALUE" fg="e1e2cf"/>
|
||||
<style token="MARKUP_COMMENT" fg="878787"/>
|
||||
<style token="MARKUP_DTD" fg="A082BD"/>
|
||||
<style token="MARKUP_PROCESSING_INSTRUCTION" fg="A082BD"/>
|
||||
<style token="MARKUP_CDATA" fg="d5e6f0"/>
|
||||
<style token="MARKUP_CDATA_DELIMITER" fg="FD971F"/>
|
||||
<style token="MARKUP_ENTITY_REFERENCE" fg="F92672"/>
|
||||
|
||||
<style token="OPERATOR" fg="A9B7C6"/>
|
||||
<style token="PREPROCESSOR" fg="A082BD"/>
|
||||
<style token="REGEX" fg="6A8759"/>
|
||||
<style token="SEPARATOR" fg="A9B7C6"/>
|
||||
<style token="VARIABLE" fg="A9B7C6" bold="false"/>
|
||||
<style token="WHITESPACE" fg="606060"/>
|
||||
|
||||
<style token="ERROR_IDENTIFIER" fg="F9F9F9" bg="d82323"/>
|
||||
<style token="ERROR_NUMBER_FORMAT" fg="F9F9F9" bg="d82323"/>
|
||||
<style token="ERROR_STRING_DOUBLE" fg="F9F9F9" bg="d82323"/>
|
||||
<style token="ERROR_CHAR" fg="F9F9F9" bg="d82323"/>
|
||||
</tokenStyles>
|
||||
|
||||
</RSyntaxTheme>
|
||||
@@ -0,0 +1,8 @@
|
||||
// Welcome to the RuneLite Development Shell
|
||||
// Everything executed here runs on the client thread by default.
|
||||
// By default client, clientThread, configManager and log are in scope
|
||||
// You can subscribe to the Event Bus by using subscribe(Event.class, ev -> handler);
|
||||
// and you can access things in the global injector module with var thing = inject(Thing.class);
|
||||
// Press Ctrl+R or F10 to execute the contents of this editor
|
||||
|
||||
log.info("Hello {}", client.getGameState());
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
import net.runelite.api.*;
|
||||
import net.runelite.api.coords.*;
|
||||
import net.runelite.api.events.*;
|
||||
import net.runelite.api.widgets.*;
|
||||
import net.runelite.client.events.*;
|
||||
import net.runelite.client.game.*;
|
||||
|
||||
var $PANEL = net.runelite.jshell.ShellPanel.INSTANCE;
|
||||
Logger log = $PANEL.getShellLogger();
|
||||
|
||||
static <T> T inject(Class<T> clazz)
|
||||
{
|
||||
return $PANEL.inject(clazz);
|
||||
}
|
||||
|
||||
static void cleanup(Runnable r)
|
||||
{
|
||||
$PANEL.cleanup(r);
|
||||
}
|
||||
|
||||
var $EVENT_BUS = inject(net.runelite.client.eventbus.EventBus.class);
|
||||
static <T> void subscribe(Class<T> eventType, Consumer<T> subscriber, float priority)
|
||||
{
|
||||
var sub = $EVENT_BUS.register(eventType, subscriber, priority);
|
||||
cleanup(() -> $EVENT_BUS.unregister(sub));
|
||||
}
|
||||
static <T> void subscribe(Class<T> eventType, Consumer<T> subscriber)
|
||||
{
|
||||
var sub = $EVENT_BUS.register(eventType, subscriber, 0.f);
|
||||
cleanup(() -> $EVENT_BUS.unregister(sub));
|
||||
}
|
||||
|
||||
var client = inject(Client.class);
|
||||
var clientThread = inject(ClientThread.class);
|
||||
var configManager = inject(ConfigManager.class);
|
||||
|
||||
Reference in New Issue
Block a user