diff --git a/.idea/runConfigurations/IDEA.xml b/.idea/runConfigurations/IDEA.xml
index 234d10d0b..89a67c4c6 100644
--- a/.idea/runConfigurations/IDEA.xml
+++ b/.idea/runConfigurations/IDEA.xml
@@ -1,9 +1,11 @@
-
+
-
-
+
+
+
+
\ No newline at end of file
diff --git a/classlib/pom.xml b/classlib/pom.xml
index dd6286a49..651f8dc76 100644
--- a/classlib/pom.xml
+++ b/classlib/pom.xml
@@ -80,6 +80,11 @@
provided
true
+
+ commons-io
+ commons-io
+ true
+
com.google.code.gson
gson
@@ -135,6 +140,15 @@
org.apache.maven.plugins
maven-javadoc-plugin
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ html/**
+
+
+
org.apache.maven.plugins
maven-shade-plugin
@@ -151,7 +165,7 @@
- cd
+
org.objectweb.asm
org.teavm.asm
diff --git a/core/pom.xml b/core/pom.xml
index 838bc758c..53fcf6dfc 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -107,10 +107,19 @@
junit:junit
- org:teavm:*
+ org.teavm:teavm-interop
+ org.teavm:teavm-metaprogramming-api
com.fasterxml.jackson.core:jackson-annotations
+
+
+ *:*
+
+ **/module-info.class
+
+
+
org.objectweb.asm
diff --git a/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java b/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java
index 1fe47c283..fff47f76f 100644
--- a/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java
+++ b/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java
@@ -79,6 +79,13 @@ public class InMemoryMethodNodeCache implements MethodNodeCache {
newAsyncItems.clear();
}
+ public void invalidate() {
+ cache.clear();
+ newItems.clear();
+ asyncCache.clear();
+ newAsyncItems.clear();
+ }
+
static final class RegularItem {
final RegularMethodNode node;
final String[] dependencies;
diff --git a/core/src/main/java/org/teavm/cache/InMemoryProgramCache.java b/core/src/main/java/org/teavm/cache/InMemoryProgramCache.java
index ee3f4c26a..bef448dc5 100644
--- a/core/src/main/java/org/teavm/cache/InMemoryProgramCache.java
+++ b/core/src/main/java/org/teavm/cache/InMemoryProgramCache.java
@@ -59,6 +59,11 @@ public class InMemoryProgramCache implements ProgramCache {
newItems.clear();
}
+ public void invalidate() {
+ cache.clear();
+ newItems.clear();
+ }
+
static final class Item {
final Program program;
final String[] dependencies;
diff --git a/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java b/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java
index 13b18515e..0a1415e5f 100644
--- a/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java
+++ b/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java
@@ -62,4 +62,9 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
cache.keySet().removeAll(classes);
freshClasses.removeAll(classes);
}
+
+ public void invalidate() {
+ cache.clear();
+ freshClasses.clear();
+ }
}
diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java
index 76442bc03..3eb5910ab 100644
--- a/core/src/main/java/org/teavm/vm/TeaVM.java
+++ b/core/src/main/java/org/teavm/vm/TeaVM.java
@@ -365,7 +365,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
dependencyAnalyzer.setAsyncSupported(target.isAsyncSupported());
dependencyAnalyzer.setInterruptor(() -> {
int progress = lastKnownClasses > 0 ? dependencyAnalyzer.getReachableClasses().size() : 0;
- return progressListener.progressReached(progress) == TeaVMProgressFeedback.CONTINUE;
+ cancelled |= progressListener.progressReached(progress) != TeaVMProgressFeedback.CONTINUE;
+ return !cancelled;
});
target.contributeDependencies(dependencyAnalyzer);
dependencyAnalyzer.processDependencies();
diff --git a/interop/core/pom.xml b/interop/core/pom.xml
index 0bd082391..cc90b3605 100644
--- a/interop/core/pom.xml
+++ b/interop/core/pom.xml
@@ -34,17 +34,6 @@
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- test-jar
-
-
-
-
org.apache.maven.plugins
maven-checkstyle-plugin
diff --git a/jso/apis/pom.xml b/jso/apis/pom.xml
index c6322aef9..d4fed183e 100644
--- a/jso/apis/pom.xml
+++ b/jso/apis/pom.xml
@@ -43,17 +43,6 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- test-jar
-
-
-
-
org.apache.maven.plugins
maven-checkstyle-plugin
diff --git a/jso/core/pom.xml b/jso/core/pom.xml
index 92644d7d4..3c1f49711 100644
--- a/jso/core/pom.xml
+++ b/jso/core/pom.xml
@@ -38,17 +38,6 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- test-jar
-
-
-
-
org.apache.maven.plugins
maven-checkstyle-plugin
diff --git a/metaprogramming/api/pom.xml b/metaprogramming/api/pom.xml
index e0a6a43df..99c3fc6fa 100644
--- a/metaprogramming/api/pom.xml
+++ b/metaprogramming/api/pom.xml
@@ -57,17 +57,6 @@
org.apache.maven.plugins
maven-javadoc-plugin
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- test-jar
-
-
-
-
\ No newline at end of file
diff --git a/metaprogramming/impl/pom.xml b/metaprogramming/impl/pom.xml
index 540698127..b9781dce7 100644
--- a/metaprogramming/impl/pom.xml
+++ b/metaprogramming/impl/pom.xml
@@ -46,6 +46,7 @@
commons-io
commons-io
+ true
org.ow2.asm
@@ -82,17 +83,6 @@
org.apache.maven.plugins
maven-javadoc-plugin
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- test-jar
-
-
-
-
org.apache.maven.plugins
maven-shade-plugin
diff --git a/pom.xml b/pom.xml
index 86f27e4e7..625f8f3b7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -197,11 +197,6 @@
rhino
${rhino.version}
-
- org.apache.maven.plugins
- maven-shade-plugin
- 3.1.1
-
@@ -286,6 +281,11 @@
maven-jar-plugin
3.0.2
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.1.1
+
diff --git a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java
index 78b207c0e..3fab3cd1c 100644
--- a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java
+++ b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java
@@ -86,7 +86,7 @@ public final class BenchmarkStarter {
private static void render() {
CanvasRenderingContext2D context = (CanvasRenderingContext2D) canvas.getContext("2d");
context.setFillStyle("white");
- context.setStrokeStyle("grey");
+ context.setStrokeStyle("red");
context.fillRect(0, 0, 600, 600);
context.save();
context.translate(0, 600);
diff --git a/tests/pom.xml b/tests/pom.xml
index 103872295..612e05f21 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -48,13 +48,6 @@
teavm-jso-apis
${project.version}
-
- org.teavm
- teavm-jso-apis
- ${project.version}
- tests
- compile
-
org.teavm
teavm-metaprogramming-impl
diff --git a/tools/cli/pom.xml b/tools/cli/pom.xml
index 678bc8fdd..d0b311d2e 100644
--- a/tools/cli/pom.xml
+++ b/tools/cli/pom.xml
@@ -110,18 +110,6 @@
maven-javadoc-plugin
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- org.teavm.cli.TeaVMRunner
-
-
-
-
-
org.apache.maven.plugins
maven-shade-plugin
@@ -134,7 +122,16 @@
+
+ org.teavm.cli.TeaVMRunner
+
+
+
+ org.apache.commons
+ org.teavm.apachecommons
+
+
diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java
index 123d6f837..fdec2f02e 100644
--- a/tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java
+++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMDevServerRunner.java
@@ -114,7 +114,7 @@ public final class TeaVMDevServerRunner {
devServer.setIndicator(commandLine.hasOption("indicator"));
devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload"));
- devServer.setVerbose(commandLine.hasOption('v'));
+ devServer.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v')));
if (commandLine.hasOption("port")) {
try {
devServer.setPort(Integer.parseInt(commandLine.getOptionValue("port")));
diff --git a/tools/core/src/main/java/org/teavm/tooling/InstructionLocationReader.java b/tools/core/src/main/java/org/teavm/tooling/InstructionLocationReader.java
index 88324964a..4a225f07d 100644
--- a/tools/core/src/main/java/org/teavm/tooling/InstructionLocationReader.java
+++ b/tools/core/src/main/java/org/teavm/tooling/InstructionLocationReader.java
@@ -15,11 +15,18 @@
*/
package org.teavm.tooling;
+import java.util.LinkedHashSet;
import java.util.Set;
+import org.teavm.model.ClassReader;
+import org.teavm.model.ClassReaderSource;
+import org.teavm.model.MethodReader;
+import org.teavm.model.MethodReference;
+import org.teavm.model.ProgramReader;
import org.teavm.model.TextLocation;
import org.teavm.model.instructions.AbstractInstructionReader;
+import org.teavm.vm.TeaVM;
-class InstructionLocationReader extends AbstractInstructionReader {
+public class InstructionLocationReader extends AbstractInstructionReader {
private Set resources;
public InstructionLocationReader(Set resources) {
@@ -32,4 +39,32 @@ class InstructionLocationReader extends AbstractInstructionReader {
resources.add(location.getFileName());
}
}
+
+ public static Set extractUsedResources(TeaVM vm) {
+ Set resources = new LinkedHashSet<>();
+ ClassReaderSource classSource = vm.getDependencyClassSource();
+ InstructionLocationReader reader = new InstructionLocationReader(resources);
+ for (MethodReference methodRef : vm.getMethods()) {
+ ClassReader cls = classSource.get(methodRef.getClassName());
+ if (cls == null) {
+ continue;
+ }
+
+ MethodReader method = cls.getMethod(methodRef.getDescriptor());
+ if (method == null) {
+ continue;
+ }
+
+ ProgramReader program = method.getProgram();
+ if (program == null) {
+ continue;
+ }
+
+ for (int i = 0; i < program.basicBlockCount(); ++i) {
+ program.basicBlockAt(i).readAllInstructions(reader);
+ }
+ }
+
+ return resources;
+ }
}
diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java
index 29b33c7bb..c7f683c14 100644
--- a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java
+++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java
@@ -52,11 +52,7 @@ import org.teavm.diagnostics.ProblemProvider;
import org.teavm.model.ClassHolderSource;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader;
-import org.teavm.model.ClassReaderSource;
-import org.teavm.model.MethodReader;
-import org.teavm.model.MethodReference;
import org.teavm.model.PreOptimizingClassHolderSource;
-import org.teavm.model.ProgramReader;
import org.teavm.parsing.ClasspathClassHolderSource;
import org.teavm.tooling.sources.SourceFileProvider;
import org.teavm.tooling.sources.SourceFilesCopier;
@@ -268,31 +264,7 @@ public class TeaVMTool {
return Collections.emptyList();
}
- Set resources = new HashSet<>();
- ClassReaderSource classSource = vm.getDependencyClassSource();
- InstructionLocationReader reader = new InstructionLocationReader(resources);
- for (MethodReference methodRef : vm.getMethods()) {
- ClassReader cls = classSource.get(methodRef.getClassName());
- if (cls == null) {
- continue;
- }
-
- MethodReader method = cls.getMethod(methodRef.getDescriptor());
- if (method == null) {
- continue;
- }
-
- ProgramReader program = method.getProgram();
- if (program == null) {
- continue;
- }
-
- for (int i = 0; i < program.basicBlockCount(); ++i) {
- program.basicBlockAt(i).readAllInstructions(reader);
- }
- }
-
- return resources;
+ return InstructionLocationReader.extractUsedResources(vm);
}
public void addSourceFileProvider(SourceFileProvider sourceFileProvider) {
diff --git a/tools/devserver/pom.xml b/tools/devserver/pom.xml
index 0bcc64922..d6ae692d8 100644
--- a/tools/devserver/pom.xml
+++ b/tools/devserver/pom.xml
@@ -66,24 +66,6 @@
teavm-tooling
${project.version}
-
- org.teavm
- teavm-classlib
- ${project.version}
- runtime
-
-
- org.teavm
- teavm-metaprogramming-impl
- ${project.version}
- runtime
-
-
- org.teavm
- teavm-jso-impl
- ${project.version}
- runtime
-
org.eclipse.jetty
diff --git a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java
index 1d0e1d026..608bb8895 100644
--- a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java
+++ b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java
@@ -96,8 +96,12 @@ public class CodeServlet extends HttpServlet {
private final Set wsEndpoints = new LinkedHashSet<>();
private final Object statusLock = new Object();
+ private volatile boolean cancelRequested;
private boolean compiling;
private double progress;
+ private boolean waiting;
+ private Thread buildThread;
+ private List listeners = new ArrayList<>();
public CodeServlet(String mainClass, String[] classPath) {
this.mainClass = mainClass;
@@ -160,6 +164,37 @@ public class CodeServlet extends HttpServlet {
}
}
+ public void addListener(DevServerListener listener) {
+ listeners.add(listener);
+ }
+
+ public void invalidateCache() {
+ synchronized (statusLock) {
+ if (compiling) {
+ return;
+ }
+ astCache.invalidate();
+ programCache.invalidate();
+ classSource.invalidate();
+ }
+ }
+
+ public void buildProject() {
+ synchronized (statusLock) {
+ if (waiting) {
+ buildThread.interrupt();
+ }
+ }
+ }
+
+ public void cancelBuild() {
+ synchronized (statusLock) {
+ if (compiling) {
+ cancelRequested = true;
+ }
+ }
+ }
+
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String path = req.getPathInfo();
@@ -206,15 +241,20 @@ public class CodeServlet extends HttpServlet {
public void destroy() {
super.destroy();
stopped = true;
+ synchronized (statusLock) {
+ if (waiting) {
+ buildThread.interrupt();
+ }
+ }
}
@Override
public void init() throws ServletException {
super.init();
Thread thread = new Thread(this::runTeaVM);
- thread.setDaemon(true);
thread.setName("TeaVM compiler");
thread.start();
+ buildThread = thread;
}
private boolean serveSourceFile(String fileName, HttpServletResponse resp) throws IOException {
@@ -306,12 +346,20 @@ public class CodeServlet extends HttpServlet {
}
try {
+ synchronized (statusLock) {
+ waiting = true;
+ }
watcher.waitForChange(750);
+ synchronized (statusLock) {
+ waiting = false;
+ }
+ log.info("Changes detected. Recompiling.");
} catch (InterruptedException e) {
- log.info("Build thread interrupted");
- break;
+ if (stopped) {
+ break;
+ }
+ log.info("Build triggered by user");
}
- log.info("Changes detected. Recompiling.");
List staleClasses = getChangedClasses(watcher.grabChangedFiles());
if (staleClasses.size() > 15) {
@@ -325,6 +373,7 @@ public class CodeServlet extends HttpServlet {
classSource.evict(staleClasses);
}
+ log.info("Build process stopped");
} catch (Throwable e) {
log.error("Compile server crashed", e);
} finally {
@@ -359,6 +408,9 @@ public class CodeServlet extends HttpServlet {
}
private void buildOnce() {
+ fireBuildStarted();
+ reportProgress(0);
+
DebugInformationBuilder debugInformationBuilder = new DebugInformationBuilder();
ClassLoader classLoader = initClassLoader();
classSource.setUnderlyingSource(new PreOptimizingClassHolderSource(
@@ -390,7 +442,6 @@ public class CodeServlet extends HttpServlet {
log.info("Starting build");
progressListener.last = 0;
progressListener.lastTime = System.currentTimeMillis();
- reportProgress(0);
vm.build(buildTarget, fileName);
addIndicator();
generateDebug(debugInformationBuilder);
@@ -450,6 +501,7 @@ public class CodeServlet extends HttpServlet {
private void postBuild(TeaVM vm, long startTime) {
if (!vm.wasCancelled()) {
log.info("Recompiled stale methods: " + programCache.getPendingItemsCount());
+ fireBuildComplete(vm);
if (vm.getProblemProvider().getSevereProblems().isEmpty()) {
log.info("Build complete successfully");
saveNewResult();
@@ -466,11 +518,13 @@ public class CodeServlet extends HttpServlet {
TeaVMProblemRenderer.describeProblems(vm, log);
} else {
log.info("Build cancelled");
+ fireBuildCancelled();
}
astCache.discard();
programCache.discard();
buildTarget.clear();
+ cancelRequested = false;
}
private void printStats(TeaVM vm, long startTime) {
@@ -553,6 +607,10 @@ public class CodeServlet extends HttpServlet {
for (CodeWsEndpoint endpoint : endpoints) {
endpoint.progress(progress);
}
+
+ for (DevServerListener listener : listeners) {
+ listener.compilationProgress(progress);
+ }
}
private void reportCompilationComplete(boolean success) {
@@ -573,6 +631,25 @@ public class CodeServlet extends HttpServlet {
}
}
+ private void fireBuildStarted() {
+ for (DevServerListener listener : listeners) {
+ listener.compilationStarted();
+ }
+ }
+
+ private void fireBuildCancelled() {
+ for (DevServerListener listener : listeners) {
+ listener.compilationCancelled();
+ }
+ }
+
+ private void fireBuildComplete(TeaVM vm) {
+ CodeServletBuildResult result = new CodeServletBuildResult(vm, new ArrayList<>(buildTarget.getNames()));
+ for (DevServerListener listener : listeners) {
+ listener.compilationComplete(result);
+ }
+ }
+
private final ProgressListenerImpl progressListener = new ProgressListenerImpl();
class ProgressListenerImpl implements TeaVMProgressListener {
@@ -622,6 +699,11 @@ public class CodeServlet extends HttpServlet {
}
private TeaVMProgressFeedback getResult() {
+ if (cancelRequested) {
+ log.info("Trying to cancel compilation due to user request");
+ return TeaVMProgressFeedback.CANCEL;
+ }
+
if (stopped) {
log.info("Trying to cancel compilation due to server stopping");
return TeaVMProgressFeedback.CANCEL;
diff --git a/tools/devserver/src/main/java/org/teavm/devserver/CodeServletBuildResult.java b/tools/devserver/src/main/java/org/teavm/devserver/CodeServletBuildResult.java
new file mode 100644
index 000000000..2df2ff254
--- /dev/null
+++ b/tools/devserver/src/main/java/org/teavm/devserver/CodeServletBuildResult.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.devserver;
+
+import java.util.Collection;
+import java.util.List;
+import org.teavm.callgraph.CallGraph;
+import org.teavm.diagnostics.ProblemProvider;
+import org.teavm.tooling.InstructionLocationReader;
+import org.teavm.tooling.builder.BuildResult;
+import org.teavm.vm.TeaVM;
+
+class CodeServletBuildResult implements BuildResult {
+ private TeaVM vm;
+ private List generatedFiles;
+ private Collection usedResources;
+
+ public CodeServletBuildResult(TeaVM vm, List generatedFiles) {
+ this.vm = vm;
+ this.generatedFiles = generatedFiles;
+ }
+
+ @Override
+ public CallGraph getCallGraph() {
+ return vm.getDependencyInfo().getCallGraph();
+ }
+
+ @Override
+ public ProblemProvider getProblems() {
+ return vm.getProblemProvider();
+ }
+
+ @Override
+ public Collection getUsedResources() {
+ if (usedResources == null) {
+ usedResources = InstructionLocationReader.extractUsedResources(vm);
+ }
+ return usedResources;
+ }
+
+ @Override
+ public Collection getClasses() {
+ return vm.getClasses();
+ }
+
+ @Override
+ public Collection getGeneratedFiles() {
+ return generatedFiles;
+ }
+}
diff --git a/tools/devserver/src/main/java/org/teavm/devserver/DevServer.java b/tools/devserver/src/main/java/org/teavm/devserver/DevServer.java
index 675e7c1dd..c7b499f6d 100644
--- a/tools/devserver/src/main/java/org/teavm/devserver/DevServer.java
+++ b/tools/devserver/src/main/java/org/teavm/devserver/DevServer.java
@@ -30,7 +30,6 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
-import org.teavm.tooling.ConsoleTeaVMToolLog;
import org.teavm.tooling.TeaVMToolLog;
public class DevServer {
@@ -41,8 +40,9 @@ public class DevServer {
private List sourcePath = new ArrayList<>();
private boolean indicator;
private boolean reloadedAutomatically;
- private boolean verbose;
private TeaVMToolLog log;
+ private CodeServlet servlet;
+ private List listeners = new ArrayList<>();
private Server server;
private int port = 9090;
@@ -85,16 +85,27 @@ public class DevServer {
this.reloadedAutomatically = reloadedAutomatically;
}
- public void setVerbose(boolean verbose) {
- this.verbose = verbose;
- }
-
public List getSourcePath() {
return sourcePath;
}
+ public void invalidateCache() {
+ servlet.invalidateCache();
+ }
+
+ public void buildProject() {
+ servlet.buildProject();
+ }
+
+ public void cancelBuild() {
+ servlet.cancelBuild();
+ }
+
+ public void addListener(DevServerListener listener) {
+ listeners.add(listener);
+ }
+
public void start() {
- log = new ConsoleTeaVMToolLog(verbose);
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(port);
@@ -103,7 +114,7 @@ public class DevServer {
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
- CodeServlet servlet = new CodeServlet(mainClass, classPath);
+ servlet = new CodeServlet(mainClass, classPath);
servlet.setFileName(fileName);
servlet.setPathToFile(pathToFile);
servlet.setLog(log);
@@ -111,6 +122,9 @@ public class DevServer {
servlet.setIndicator(indicator);
servlet.setAutomaticallyReloaded(reloadedAutomatically);
servlet.setPort(port);
+ for (DevServerListener listener : listeners) {
+ servlet.addListener(listener);
+ }
context.addServlet(new ServletHolder(servlet), "/*");
try {
@@ -129,6 +143,8 @@ public class DevServer {
} catch (Exception e) {
throw new RuntimeException(e);
}
+ server = null;
+ servlet = null;
}
private class DevServerEndpointConfig implements ServerEndpointConfig {
diff --git a/tools/devserver/src/main/java/org/teavm/devserver/DevServerListener.java b/tools/devserver/src/main/java/org/teavm/devserver/DevServerListener.java
new file mode 100644
index 000000000..6250a6a3a
--- /dev/null
+++ b/tools/devserver/src/main/java/org/teavm/devserver/DevServerListener.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.devserver;
+
+import org.teavm.tooling.builder.BuildResult;
+
+public interface DevServerListener {
+ void compilationStarted();
+
+ void compilationProgress(double progress);
+
+ void compilationComplete(BuildResult result);
+
+ void compilationCancelled();
+}
diff --git a/tools/idea/idea-artifacts/dep-pom.xml b/tools/idea/idea-artifacts/dep-pom.xml
index 7f01c8492..f564b4289 100644
--- a/tools/idea/idea-artifacts/dep-pom.xml
+++ b/tools/idea/idea-artifacts/dep-pom.xml
@@ -44,23 +44,21 @@
org.teavm
teavm-tooling
${teavm.version}
-
-
- org.slf4j
- slf4j-api
-
-
+
+
+ org.teavm
+ teavm-devserver
+ ${teavm.version}
+
+
+ org.teavm
+ teavm-classlib
+ ${teavm.version}
org.teavm
teavm-chrome-rdp
${teavm.version}
-
-
- org.slf4j
- slf4j-api
-
-
@@ -125,6 +123,36 @@
false
dependencies/teavm.jar
+
+
+ com.google.gson
+ org.teavm.shade.gson
+
+
+ com.jcraft.jzlib
+ org.teavm.shade.jzlib
+
+
+ org.eclipse.jetty
+ org.teavm.shade.jetty
+
+
+ org.joda.time
+ org.teavm.shade.jodatime
+
+
+ org.objectweb.asm
+ org.teavm.shade.jetty.asm
+
+
+ org.slf4j
+ org.teavm.shade.slf4j
+
+
+ org.apache.commons
+ org.teavm.apachecommons
+
+
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/DaemonUtil.java b/tools/idea/plugin/src/main/java/org/teavm/idea/DaemonUtil.java
index 78cc05ab7..d0d36e0a2 100644
--- a/tools/idea/plugin/src/main/java/org/teavm/idea/DaemonUtil.java
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/DaemonUtil.java
@@ -24,13 +24,16 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import org.teavm.idea.devserver.DevServerRunner;
import org.teavm.tooling.daemon.BuildDaemon;
public final class DaemonUtil {
private static final Set PLUGIN_FILES = new HashSet<>(Arrays.asList("teavm-jps-common.jar",
"teavm-plugin.jar", "teavm.jar"));
private static final String DAEMON_CLASS = BuildDaemon.class.getName().replace('.', '/') + ".class";
+ private static final String DEV_SERVER_CLASS = DevServerRunner.class.getName().replace('.', '/') + ".class";
private static final int DAEMON_CLASS_DEPTH;
+ private static final int DEV_SERVER_CLASS_DEPTH;
static {
int depth = 0;
@@ -40,6 +43,14 @@ public final class DaemonUtil {
}
}
DAEMON_CLASS_DEPTH = depth;
+
+ depth = 0;
+ for (int i = 0; i < DEV_SERVER_CLASS.length(); ++i) {
+ if (DEV_SERVER_CLASS.charAt(i) == '/') {
+ depth++;
+ }
+ }
+ DEV_SERVER_CLASS_DEPTH = depth;
}
private DaemonUtil() {
@@ -64,6 +75,11 @@ public final class DaemonUtil {
file = file.getParentFile();
}
targetFiles.add(file.getAbsolutePath());
+ } else if (file.getPath().endsWith(DEV_SERVER_CLASS)) {
+ for (int i = 0; i <= DEV_SERVER_CLASS_DEPTH; ++i) {
+ file = file.getParentFile();
+ }
+ targetFiles.add(file.getAbsolutePath());
} else if (file.isDirectory()) {
for (File childFile : file.listFiles()) {
findInHierarchy(childFile, targetFiles, visited);
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/DevServerRunnerListener.java b/tools/idea/plugin/src/main/java/org/teavm/idea/DevServerRunnerListener.java
new file mode 100644
index 000000000..94b31fc1e
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/DevServerRunnerListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea;
+
+public interface DevServerRunnerListener {
+ void error(String text);
+
+ void info(String text);
+
+ void stopped(int code);
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/debug/TeaVMDebugConfiguration.java b/tools/idea/plugin/src/main/java/org/teavm/idea/debug/TeaVMDebugConfiguration.java
index 676e5e545..fe5544202 100644
--- a/tools/idea/plugin/src/main/java/org/teavm/idea/debug/TeaVMDebugConfiguration.java
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/debug/TeaVMDebugConfiguration.java
@@ -15,7 +15,6 @@
*/
package org.teavm.idea.debug;
-import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.LocatableConfigurationBase;
@@ -45,8 +44,7 @@ public class TeaVMDebugConfiguration extends LocatableConfigurationBase
@Nullable
@Override
- public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws
- ExecutionException {
+ public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
return new TeaVMRunState(environment, port);
}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerBuildResult.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerBuildResult.java
new file mode 100644
index 000000000..5989d58e3
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerBuildResult.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import org.teavm.callgraph.CallGraph;
+import org.teavm.diagnostics.Problem;
+
+public class DevServerBuildResult implements Serializable {
+ public CallGraph callGraph;
+ public final List problems = new ArrayList<>();
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerConfiguration.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerConfiguration.java
new file mode 100644
index 000000000..656ce43ff
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerConfiguration.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+public class DevServerConfiguration {
+ public String javaHome;
+ public int maxHeap;
+ public String mainClass;
+ public String[] classPath;
+ public String[] sourcePath;
+ public boolean indicator;
+ public boolean autoReload;
+ public int port;
+ public String pathToFile;
+ public String fileName;
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerInfo.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerInfo.java
new file mode 100644
index 000000000..d0cc2d6c7
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerInfo.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+public class DevServerInfo {
+ public final int port;
+ public final DevServerManager server;
+ public final Process process;
+
+ DevServerInfo(int port, DevServerManager server, Process process) {
+ this.port = port;
+ this.server = server;
+ this.process = process;
+ }
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerManager.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerManager.java
new file mode 100644
index 000000000..6341981dd
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerManager.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface DevServerManager extends Remote {
+ String ID = "TeaVM-BuildServer";
+
+ void stop() throws RemoteException;
+
+ void invalidateCache() throws RemoteException;
+
+ void buildProject() throws RemoteException;
+
+ void cancelBuild() throws RemoteException;
+
+ void addListener(DevServerManagerListener listener) throws RemoteException;
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerManagerListener.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerManagerListener.java
new file mode 100644
index 000000000..019345b6c
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerManagerListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface DevServerManagerListener extends Remote {
+ void compilationStarted() throws RemoteException;
+
+ void compilationProgress(double progress) throws RemoteException;
+
+ void compilationComplete(DevServerBuildResult result) throws RemoteException;
+
+ void compilationCancelled() throws RemoteException;
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerRunner.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerRunner.java
new file mode 100644
index 000000000..ac0ffff1e
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/DevServerRunner.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.rmi.AlreadyBoundException;
+import java.rmi.NoSuchObjectException;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Consumer;
+import org.teavm.devserver.DevServer;
+import org.teavm.devserver.DevServerListener;
+import org.teavm.idea.DevServerRunnerListener;
+import org.teavm.tooling.ConsoleTeaVMToolLog;
+import org.teavm.tooling.builder.BuildResult;
+
+public class DevServerRunner extends UnicastRemoteObject implements DevServerManager {
+ private static final int MIN_PORT = 10000;
+ private static final int MAX_PORT = 1 << 16;
+ private static final String PORT_MESSAGE_PREFIX = "Build server port: ";
+ private static final String DEBUG_PORT_PROPERTY = "teavm.server.debug.port";
+ private int port;
+ private Registry registry;
+ private DevServer server;
+ private List listeners = new ArrayList<>();
+
+ private DevServerRunner(DevServer server) throws RemoteException {
+ super();
+ Random random = new Random();
+ for (int i = 0; i < 20; ++i) {
+ port = random.nextInt(MAX_PORT - MIN_PORT) + MIN_PORT;
+ try {
+ registry = LocateRegistry.createRegistry(port);
+ } catch (RemoteException e) {
+ continue;
+ }
+ try {
+ registry.bind(ID, this);
+ } catch (RemoteException | AlreadyBoundException e) {
+ throw new IllegalStateException("Could not bind remote service", e);
+ }
+
+ this.server = server;
+ server.addListener(devServerListener);
+
+ return;
+ }
+ throw new IllegalStateException("Could not create RMI registry");
+ }
+
+ @Override
+ public void stop() {
+ server.stop();
+ }
+
+ @Override
+ public void invalidateCache() {
+ server.invalidateCache();
+ }
+
+ @Override
+ public void buildProject() {
+ server.buildProject();
+ }
+
+ @Override
+ public void cancelBuild() {
+ server.cancelBuild();
+ }
+
+ @Override
+ public void addListener(DevServerManagerListener listener) {
+ listeners.add(listener);
+ }
+
+ public static void main(String[] args) throws Exception {
+ DevServer server = new DevServer();
+ server.setLog(new ConsoleTeaVMToolLog(true));
+ server.setMainClass(args[0]);
+ List classPath = new ArrayList<>();
+ for (int i = 1; i < args.length; ++i) {
+ switch (args[i]) {
+ case "-c":
+ classPath.add(args[++i]);
+ break;
+ case "-s":
+ server.getSourcePath().add(args[++i]);
+ break;
+ case "-i":
+ server.setIndicator(true);
+ break;
+ case "-a":
+ server.setReloadedAutomatically(true);
+ break;
+ case "-p":
+ server.setPort(Integer.parseInt(args[++i]));
+ break;
+ case "-d":
+ server.setPathToFile(args[++i]);
+ break;
+ case "-f":
+ server.setFileName(args[++i]);
+ break;
+ }
+ }
+ server.setClassPath(classPath.toArray(new String[0]));
+
+ DevServerRunner daemon = new DevServerRunner(server);
+ System.out.println(PORT_MESSAGE_PREFIX + daemon.port);
+ server.start();
+
+ try {
+ daemon.registry.unbind(ID);
+ UnicastRemoteObject.unexportObject(daemon, true);
+ } catch (NoSuchObjectException e) {
+ throw new IllegalStateException("Could not shutdown RMI registry", e);
+ }
+ }
+
+ public static DevServerInfo start(String[] classPathEntries, DevServerConfiguration options,
+ DevServerRunnerListener listener) throws IOException {
+ String javaCommand = options.javaHome + "/bin/java";
+ String classPath = String.join(File.pathSeparator, classPathEntries);
+ List arguments = new ArrayList<>();
+
+ arguments.addAll(Arrays.asList(javaCommand, "-cp", classPath, "-Xmx" + options.maxHeap + "m"));
+
+ String debugPort = System.getProperty(DEBUG_PORT_PROPERTY);
+ if (debugPort != null) {
+ arguments.add("-agentlib:jdwp=transport=dt_socket,quiet=y,server=y,address=" + debugPort + ",suspend=y");
+ }
+
+ arguments.add(DevServerRunner.class.getName());
+ arguments.add(options.mainClass);
+
+ if (options.indicator) {
+ arguments.add("-i");
+ }
+ if (options.autoReload) {
+ arguments.add("-a");
+ }
+ arguments.add("-d");
+ arguments.add(options.pathToFile);
+ arguments.add("-f");
+ arguments.add(options.fileName);
+ arguments.add("-p");
+ arguments.add(Integer.toString(options.port));
+
+ for (String entry : options.classPath) {
+ arguments.add("-c");
+ arguments.add(entry);
+ }
+ for (String entry : options.sourcePath) {
+ arguments.add("-s");
+ arguments.add(entry);
+ }
+
+ ProcessBuilder builder = new ProcessBuilder(arguments.toArray(new String[0]));
+ Process process = builder.start();
+ BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream(),
+ StandardCharsets.UTF_8));
+ BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream(),
+ StandardCharsets.UTF_8));
+ String line = stdoutReader.readLine();
+ if (line == null || !line.startsWith(PORT_MESSAGE_PREFIX)) {
+ StringBuilder sb = new StringBuilder();
+ while (true) {
+ line = stderrReader.readLine();
+ if (line == null) {
+ break;
+ }
+ sb.append(line).append('\n');
+ }
+ stderrReader.close();
+ stdoutReader.close();
+ process.destroy();
+ throw new IllegalStateException("Could not start daemon. Stderr: " + sb);
+ }
+ int port = Integer.parseInt(line.substring(PORT_MESSAGE_PREFIX.length()));
+
+ daemonThread("TeaVM devserver stdout", new ProcessOutputWatcher(stdoutReader, listener::info)).start();
+ daemonThread("TeaVM devserver stderr", new ProcessOutputWatcher(stderrReader, listener::error)).start();
+ daemonThread("TeaVM devserver monitor", () -> {
+ int exitCode;
+ try {
+ exitCode = process.waitFor();
+ } catch (InterruptedException e) {
+ return;
+ }
+ listener.stopped(exitCode);
+ }).start();
+
+ DevServerManager service;
+ try {
+ Registry registry = LocateRegistry.getRegistry(port);
+ service = (DevServerManager) registry.lookup(ID);
+ } catch (RemoteException | NotBoundException e) {
+ throw new RuntimeException("Error connecting TeaVM process", e);
+ }
+
+ return new DevServerInfo(port, service, process);
+ }
+
+ private static Thread daemonThread(String name, Runnable runnable) {
+ Thread thread = new Thread(runnable);
+ thread.setName(name);
+ thread.setDaemon(true);
+ return thread;
+ }
+
+ static class ProcessOutputWatcher implements Runnable {
+ private BufferedReader reader;
+ private Consumer consumer;
+
+ ProcessOutputWatcher(BufferedReader reader, Consumer consumer) {
+ this.reader = reader;
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ String line = reader.readLine();
+ if (line == null) {
+ break;
+ }
+ consumer.accept(line);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ final DevServerListener devServerListener = new DevServerListener() {
+ @Override
+ public void compilationStarted() {
+ for (DevServerManagerListener listener : listeners) {
+ try {
+ listener.compilationStarted();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void compilationProgress(double v) {
+ for (DevServerManagerListener listener : listeners) {
+ try {
+ listener.compilationProgress(v);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void compilationComplete(BuildResult buildResult) {
+ DevServerBuildResult result = new DevServerBuildResult();
+ result.callGraph = buildResult.getCallGraph();
+ result.problems.addAll(buildResult.getProblems().getProblems());
+ for (DevServerManagerListener listener : listeners) {
+ try {
+ listener.compilationComplete(result);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void compilationCancelled() {
+ for (DevServerManagerListener listener : listeners) {
+ try {
+ listener.compilationCancelled();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerConfiguration.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerConfiguration.java
new file mode 100644
index 000000000..7e505d2eb
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerConfiguration.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.ModuleBasedConfiguration;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.configurations.RunConfigurationModule;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.facet.FacetManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.util.xmlb.XmlSerializer;
+import com.intellij.util.xmlb.annotations.Property;
+import com.intellij.util.xmlb.annotations.Tag;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.teavm.idea.TeaVMFacetType;
+import org.teavm.idea.devserver.ui.TeaVMDevServerSettingsEditor;
+
+public class TeaVMDevServerConfiguration
+ extends ModuleBasedConfiguration {
+ private String mainClass = "";
+ private String jdkPath;
+ private int port = 9090;
+ private String pathToFile = "";
+ private String fileName = "classes.js";
+ private boolean indicator = true;
+ private boolean automaticallyReloaded;
+ private int maxHeap = 1024;
+
+ public TeaVMDevServerConfiguration(
+ @NotNull RunConfigurationModule configurationModule,
+ @NotNull ConfigurationFactory factory) {
+ super("TeaVM dev server", configurationModule, factory);
+ }
+
+ @Override
+ public Collection getValidModules() {
+ Module[] modules = ModuleManager.getInstance(getProject()).getModules();
+ List validModules = new ArrayList<>();
+ for (Module module : modules) {
+ FacetManager facetManager = FacetManager.getInstance(module);
+ if (facetManager.getFacetByType(TeaVMFacetType.TYPE_ID) != null) {
+ validModules.add(module);
+ }
+ }
+ return validModules;
+ }
+
+ @Override
+ public void readExternal(@NotNull Element element) throws InvalidDataException {
+ super.readExternal(element);
+
+ Element child = element.getChild("teavm");
+ if (child != null) {
+ XmlSerializer.deserializeInto(this, child);
+ }
+ }
+
+ @Override
+ public void writeExternal(@NotNull Element element) throws WriteExternalException {
+ super.writeExternal(element);
+
+ Element child = element.getChild("teavm");
+ if (child == null) {
+ child = new Element("teavm");
+ element.addContent(child);
+ }
+ XmlSerializer.serializeInto(this, child, (accessor, bean) ->
+ !accessor.getName().equals("isAllowRunningInParallel"));
+ }
+
+ @NotNull
+ @Override
+ public SettingsEditor extends RunConfiguration> getConfigurationEditor() {
+ return new TeaVMDevServerSettingsEditor(getProject());
+ }
+
+ @Nullable
+ @Override
+ public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
+ return new TeaVMDevServerRunState(environment, this);
+ }
+
+ @Property
+ @Tag
+ public String getMainClass() {
+ return mainClass;
+ }
+
+ public void setMainClass(String mainClass) {
+ this.mainClass = mainClass;
+ }
+
+ @Property
+ @Tag
+ public String getJdkPath() {
+ return jdkPath;
+ }
+
+ public void setJdkPath(String jdkPath) {
+ this.jdkPath = jdkPath;
+ }
+
+ @Property
+ @Tag
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ @Property
+ @Tag
+ public String getPathToFile() {
+ return pathToFile;
+ }
+
+ public void setPathToFile(String pathToFile) {
+ this.pathToFile = pathToFile;
+ }
+
+ @Property
+ @Tag
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ @Property
+ @Tag
+ public boolean isIndicator() {
+ return indicator;
+ }
+
+ public void setIndicator(boolean indicator) {
+ this.indicator = indicator;
+ }
+
+ @Property
+ @Tag
+ public boolean isAutomaticallyReloaded() {
+ return automaticallyReloaded;
+ }
+
+ public void setAutomaticallyReloaded(boolean automaticallyReloaded) {
+ this.automaticallyReloaded = automaticallyReloaded;
+ }
+
+ @Property
+ @Tag
+ public int getMaxHeap() {
+ return maxHeap;
+ }
+
+ public void setMaxHeap(int maxHeap) {
+ this.maxHeap = maxHeap;
+ }
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerConfigurationType.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerConfigurationType.java
new file mode 100644
index 000000000..07ef1167c
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerConfigurationType.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.ConfigurationTypeBase;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.configurations.RunConfigurationModule;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.IconLoader;
+import org.jetbrains.annotations.NotNull;
+
+public class TeaVMDevServerConfigurationType extends ConfigurationTypeBase {
+ public TeaVMDevServerConfigurationType() {
+ super("TeaVMDevServerConfiguration", "TeaVM development server", "TeaVM development server"
+ + "agent", IconLoader.getIcon("/teavm-16.png"));
+ }
+
+ private final ConfigurationFactory factory = new ConfigurationFactory(this) {
+ @NotNull
+ @Override
+ public RunConfiguration createTemplateConfiguration(@NotNull Project project) {
+ return new TeaVMDevServerConfiguration(new RunConfigurationModule(project), this);
+ }
+ };
+
+ @Override
+ public ConfigurationFactory[] getConfigurationFactories() {
+ return new ConfigurationFactory[] { factory };
+ }
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerRunState.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerRunState.java
new file mode 100644
index 000000000..3c7ff10b9
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerRunState.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+import com.intellij.execution.DefaultExecutionResult;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.configurations.SearchScopeProvider;
+import com.intellij.execution.filters.TextConsoleBuilder;
+import com.intellij.execution.filters.TextConsoleBuilderFactory;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.execution.ui.ConsoleView;
+import com.intellij.execution.ui.ConsoleViewContentType;
+import com.intellij.execution.util.JavaParametersUtil;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.OrderEnumerator;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.search.GlobalSearchScope;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.teavm.idea.DaemonUtil;
+import org.teavm.idea.DevServerRunnerListener;
+
+public class TeaVMDevServerRunState implements RunProfileState {
+ private final TeaVMDevServerConfiguration configuration;
+ private final TextConsoleBuilder consoleBuilder;
+
+ public TeaVMDevServerRunState(@NotNull ExecutionEnvironment environment,
+ @NotNull TeaVMDevServerConfiguration configuration) {
+ this.configuration = configuration;
+
+ Project project = environment.getProject();
+ GlobalSearchScope searchScope = SearchScopeProvider.createSearchScope(project, environment.getRunProfile());
+ consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project, searchScope);
+ }
+
+ @Nullable
+ @Override
+ public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
+ DevServerConfiguration config = new DevServerConfiguration();
+ Module module = configuration.getConfigurationModule().getModule();
+
+ Sdk moduleSdk = JavaParametersUtil.createModuleJdk(module, true, configuration.getJdkPath());
+ config.javaHome = moduleSdk.getHomePath();
+ OrderEnumerator enumerator = OrderEnumerator.orderEntries(module).withoutSdk().recursively();
+ config.classPath = Arrays.stream(enumerator.getClassesRoots())
+ .map(this::path)
+ .filter(Objects::nonNull)
+ .toArray(String[]::new);
+ config.sourcePath = Arrays.stream(enumerator.getSourceRoots())
+ .map(this::path)
+ .filter(Objects::nonNull)
+ .toArray(String[]::new);
+ config.pathToFile = configuration.getPathToFile();
+ config.fileName = configuration.getFileName();
+ config.port = configuration.getPort();
+ config.indicator = configuration.isIndicator();
+ config.autoReload = configuration.isAutomaticallyReloaded();
+ config.mainClass = configuration.getMainClass();
+ config.maxHeap = configuration.getMaxHeap();
+
+ try {
+ ConsoleView console = consoleBuilder.getConsole();
+ ProcessHandlerImpl processHandler = new ProcessHandlerImpl(config, console);
+ console.attachToProcess(processHandler);
+ processHandler.start();
+ return new DefaultExecutionResult(console, processHandler);
+ } catch (IOException e) {
+ throw new ExecutionException(e);
+ }
+ }
+
+ private String path(VirtualFile file) {
+ while (file.getFileSystem() instanceof JarFileSystem) {
+ file = ((JarFileSystem) file.getFileSystem()).getLocalByEntry(file);
+ if (file == null) {
+ return null;
+ }
+ }
+ return file.getCanonicalPath();
+ }
+
+ class ProcessHandlerImpl extends ProcessHandler implements DevServerRunnerListener {
+ private DevServerConfiguration config;
+ private ConsoleView console;
+ private DevServerInfo info;
+
+ ProcessHandlerImpl(DevServerConfiguration config, ConsoleView console) {
+ this.config = config;
+ this.console = console;
+ }
+
+ void start() throws IOException {
+ info = DevServerRunner.start(DaemonUtil.detectClassPath().toArray(new String[0]), config, this);
+ }
+
+ @Override
+ protected void destroyProcessImpl() {
+ info.process.destroy();
+ }
+
+ @Override
+ protected void detachProcessImpl() {
+ try {
+ info.server.stop();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean detachIsDefault() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public OutputStream getProcessInput() {
+ return null;
+ }
+
+
+ @Override
+ public void error(String text) {
+ console.print(text + System.lineSeparator(), ConsoleViewContentType.ERROR_OUTPUT);
+ }
+
+ @Override
+ public void info(String text) {
+ console.print(text + System.lineSeparator(), ConsoleViewContentType.NORMAL_OUTPUT);
+ }
+
+ @Override
+ public void stopped(int code) {
+ notifyProcessTerminated(code);
+ }
+ }
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerRunner.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerRunner.java
new file mode 100644
index 000000000..588f8e554
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/TeaVMDevServerRunner.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.configurations.RunnerSettings;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.GenericProgramRunner;
+import com.intellij.execution.ui.RunContentDescriptor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class TeaVMDevServerRunner extends GenericProgramRunner {
+ @NotNull
+ @Override
+ public String getRunnerId() {
+ return "TeaVMDevServerRunner";
+ }
+
+ @Override
+ public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
+ return profile instanceof TeaVMDevServerConfiguration;
+ }
+
+ @Nullable
+ @Override
+ protected RunContentDescriptor doExecute(@NotNull RunProfileState state,
+ @NotNull ExecutionEnvironment environment) throws ExecutionException {
+ ExecutionResult executionResult = state.execute(environment.getExecutor(), environment.getRunner());
+ if (executionResult == null) {
+ return null;
+ }
+
+ return null;
+ }
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/ui/TeaVMDevServerSettingsEditor.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/ui/TeaVMDevServerSettingsEditor.java
new file mode 100644
index 000000000..efca85d75
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/ui/TeaVMDevServerSettingsEditor.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver.ui;
+
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.project.Project;
+import javax.swing.JComponent;
+import org.jetbrains.annotations.NotNull;
+import org.teavm.idea.devserver.TeaVMDevServerConfiguration;
+
+public class TeaVMDevServerSettingsEditor extends SettingsEditor {
+ private final Project project;
+ private TeaVMDevServerSettingsPanel panel;
+
+ public TeaVMDevServerSettingsEditor(Project project) {
+ this.project = project;
+ }
+
+ @Override
+ protected void resetEditorFrom(@NotNull TeaVMDevServerConfiguration s) {
+ if (panel == null) {
+ return;
+ }
+
+ panel.load(s);
+ }
+
+ @Override
+ protected void applyEditorTo(@NotNull TeaVMDevServerConfiguration s) {
+ if (panel == null) {
+ return;
+ }
+
+ panel.save(s);
+ }
+
+ @Override
+ protected void disposeEditor() {
+ if (panel != null) {
+ panel = null;
+ }
+ super.disposeEditor();
+ }
+
+ @NotNull
+ @Override
+ protected JComponent createEditor() {
+ if (panel == null) {
+ panel = new TeaVMDevServerSettingsPanel(project);
+ }
+ return panel;
+ }
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/ui/TeaVMDevServerSettingsPanel.java b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/ui/TeaVMDevServerSettingsPanel.java
new file mode 100644
index 000000000..1369cee88
--- /dev/null
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/devserver/ui/TeaVMDevServerSettingsPanel.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2018 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.idea.devserver.ui;
+
+import com.intellij.application.options.ModuleDescriptionsComboBox;
+import com.intellij.execution.configurations.ConfigurationUtil;
+import com.intellij.execution.ui.ConfigurationModuleSelector;
+import com.intellij.execution.ui.DefaultJreSelector;
+import com.intellij.execution.ui.JrePathEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaCodeFragment;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.util.PsiMethodUtil;
+import com.intellij.ui.EditorTextFieldWithBrowseButton;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.text.DecimalFormat;
+import javax.swing.JCheckBox;
+import javax.swing.JFormattedTextField;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+import org.teavm.idea.devserver.TeaVMDevServerConfiguration;
+
+public class TeaVMDevServerSettingsPanel extends JPanel {
+ private final JrePathEditor jrePathEditor;
+
+ private final ModuleDescriptionsComboBox moduleField;
+ private final ConfigurationModuleSelector moduleSelector;
+
+ private EditorTextFieldWithBrowseButton mainClassField;
+
+ private JFormattedTextField portField;
+ private JTextField pathToFileField;
+ private JTextField fileNameField;
+ private JCheckBox indicatorField;
+ private JCheckBox autoReloadField;
+ private JFormattedTextField maxHeapField;
+
+ public TeaVMDevServerSettingsPanel(Project project) {
+ moduleField = new ModuleDescriptionsComboBox();
+ moduleSelector = new ConfigurationModuleSelector(project, moduleField);
+
+ JavaCodeFragment.VisibilityChecker visibilityChecker = (declaration, place) -> {
+ if (declaration instanceof PsiClass) {
+ PsiClass cls = (PsiClass) declaration;
+ if (ConfigurationUtil.MAIN_CLASS.value(cls) && PsiMethodUtil.findMainMethod(cls) != null
+ || place.getParent() != null && moduleSelector.findClass(cls.getQualifiedName()) != null) {
+ return JavaCodeFragment.VisibilityChecker.Visibility.VISIBLE;
+ }
+ }
+ return JavaCodeFragment.VisibilityChecker.Visibility.NOT_VISIBLE;
+ };
+ mainClassField = new EditorTextFieldWithBrowseButton(project, true, visibilityChecker);
+ mainClassField.setButtonEnabled(true);
+
+ jrePathEditor = new JrePathEditor(DefaultJreSelector.fromSourceRootsDependencies(moduleField, mainClassField));
+
+ portField = new JFormattedTextField(new DecimalFormat("#0"));
+ fileNameField = new JTextField();
+ pathToFileField = new JTextField();
+ indicatorField = new JCheckBox("Display indicator on a web page:");
+ autoReloadField = new JCheckBox("Reload page automatically:");
+ maxHeapField = new JFormattedTextField(new DecimalFormat("#0"));
+
+ initLayout();
+ }
+
+ private void initLayout() {
+ setLayout(new GridBagLayout());
+ setBorder(new EmptyBorder(10, 10, 10, 10));
+
+ GridBagConstraints labelConstraints = new GridBagConstraints();
+ labelConstraints.insets.right = 5;
+ labelConstraints.anchor = GridBagConstraints.LINE_START;
+
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.gridwidth = GridBagConstraints.REMAINDER;
+ constraints.fill = GridBagConstraints.HORIZONTAL;
+ constraints.weightx = 1;
+ constraints.insets.top = 4;
+ constraints.insets.bottom = 4;
+
+ add(new JLabel("Main class:"), labelConstraints);
+ add(mainClassField, constraints);
+
+ add(new JLabel("Use classpath of module:"), labelConstraints);
+ add(moduleField, constraints);
+
+ add(jrePathEditor, constraints);
+
+ add(new JLabel("Port:"), labelConstraints);
+ add(portField, constraints);
+
+ add(new JLabel("File name:"), labelConstraints);
+ add(fileNameField, constraints);
+
+ add(new JLabel("Path to file:"), labelConstraints);
+ add(pathToFileField, constraints);
+
+ add(indicatorField, constraints);
+ add(autoReloadField, constraints);
+
+ add(new JLabel("Server heap limit:"), labelConstraints);
+ add(maxHeapField, constraints);
+ }
+
+ public void load(TeaVMDevServerConfiguration configuration) {
+ mainClassField.setText(configuration.getMainClass());
+ moduleSelector.reset(configuration);
+ jrePathEditor.setPathOrName(configuration.getJdkPath(), false);
+ fileNameField.setText(configuration.getFileName());
+ pathToFileField.setText(configuration.getPathToFile());
+ indicatorField.setSelected(configuration.isIndicator());
+ autoReloadField.setSelected(configuration.isAutomaticallyReloaded());
+ maxHeapField.setText(Integer.toString(configuration.getMaxHeap()));
+ portField.setText(Integer.toString(configuration.getPort()));
+ }
+
+ public void save(TeaVMDevServerConfiguration configuration) {
+ configuration.setMainClass(mainClassField.getText());
+ moduleSelector.applyTo(configuration);
+ configuration.setJdkPath(jrePathEditor.getJrePathOrName());
+ configuration.setFileName(fileNameField.getText());
+ configuration.setPathToFile(pathToFileField.getText());
+ configuration.setIndicator(indicatorField.isSelected());
+ configuration.setAutomaticallyReloaded(autoReloadField.isSelected());
+ configuration.setMaxHeap(Integer.parseInt(maxHeapField.getText()));
+ configuration.setPort(Integer.parseInt(portField.getText()));
+ }
+}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/maven/TeaVMMavenImporter.java b/tools/idea/plugin/src/main/java/org/teavm/idea/maven/TeaVMMavenImporter.java
index ca3d8ddf7..486b564d1 100644
--- a/tools/idea/plugin/src/main/java/org/teavm/idea/maven/TeaVMMavenImporter.java
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/maven/TeaVMMavenImporter.java
@@ -110,6 +110,9 @@ public class TeaVMMavenImporter extends MavenImporter {
}
TeaVMJpsConfiguration configuration = facet.getConfiguration().getState();
+ if (justCreated) {
+ configuration.setSkipped(true);
+ }
for (Element child : source.getChildren()) {
switch (child.getName()) {
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMConfigurable.java b/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMConfigurable.java
index d9b78e9d7..80f1a4277 100644
--- a/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMConfigurable.java
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMConfigurable.java
@@ -17,7 +17,6 @@ package org.teavm.idea.ui;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.Configurable;
-import com.intellij.openapi.options.ConfigurationException;
import javax.swing.JComponent;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable;
@@ -60,7 +59,7 @@ public class TeaVMConfigurable implements Configurable {
}
@Override
- public void apply() throws ConfigurationException {
+ public void apply() {
panel.save(configuration);
}
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMFacetEditorTab.java b/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMFacetEditorTab.java
index 3a8a76b6f..1ce97f712 100644
--- a/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMFacetEditorTab.java
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMFacetEditorTab.java
@@ -17,7 +17,6 @@ package org.teavm.idea.ui;
import com.intellij.facet.ui.FacetEditorTab;
import com.intellij.openapi.module.Module;
-import com.intellij.openapi.options.ConfigurationException;
import javax.swing.JComponent;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -48,7 +47,7 @@ public class TeaVMFacetEditorTab extends FacetEditorTab {
}
@Override
- public void apply() throws ConfigurationException {
+ public void apply() {
configurable.apply();
}
diff --git a/tools/idea/plugin/src/main/resources/META-INF/plugin.xml b/tools/idea/plugin/src/main/resources/META-INF/plugin.xml
index 946e6c838..8cb8f7c95 100644
--- a/tools/idea/plugin/src/main/resources/META-INF/plugin.xml
+++ b/tools/idea/plugin/src/main/resources/META-INF/plugin.xml
@@ -36,6 +36,9 @@
+
+
+