From ca5239788183e394f19c080734bc7955225d7a17 Mon Sep 17 00:00:00 2001 From: Artem Bukhonov Date: Wed, 18 Jan 2017 01:15:53 +0300 Subject: [PATCH] platform: added async version of execute() in RunProfileStarter It allows to create asynchronous run profiles which can be started after BeforeRun tasks (e.g. Build) --- .../intellij/execution/RunProfileStarter.java | 18 ++++ .../runners/AsyncGenericProgramRunner.java | 8 ++ .../execution/impl/ExecutionManagerImpl.java | 93 +++++++++---------- 3 files changed, 71 insertions(+), 48 deletions(-) diff --git a/platform/lang-api/src/com/intellij/execution/RunProfileStarter.java b/platform/lang-api/src/com/intellij/execution/RunProfileStarter.java index 59456db49572..72ab5b5d5b2b 100644 --- a/platform/lang-api/src/com/intellij/execution/RunProfileStarter.java +++ b/platform/lang-api/src/com/intellij/execution/RunProfileStarter.java @@ -20,6 +20,9 @@ import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.ui.RunContentDescriptor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.concurrency.Promise; + +import static org.jetbrains.concurrency.Promises.rejectedPromise; /** * The callback used to execute a process from the {@link ExecutionManager#startRunProfile(RunProfileStarter, com.intellij.execution.configurations.RunProfileState, com.intellij.execution.runners.ExecutionEnvironment)} @@ -29,4 +32,19 @@ import org.jetbrains.annotations.Nullable; public abstract class RunProfileStarter { @Nullable public abstract RunContentDescriptor execute(@NotNull RunProfileState state, @NotNull ExecutionEnvironment environment) throws ExecutionException; + + /** + * Async version of {@link #execute(RunProfileState, ExecutionEnvironment)}. + * You must NOT throw exceptions in this method. + * Instead return {@link org.jetbrains.concurrency.Promises#rejectedPromise(Throwable)} or call {@link org.jetbrains.concurrency.AsyncPromise#setError(Throwable)} + */ + public Promise executeAsync(@NotNull RunProfileState state, @NotNull ExecutionEnvironment environment) { + try { + RunContentDescriptor result = execute(state, environment); + return Promise.resolve(result); + } + catch (ExecutionException e) { + return rejectedPromise(e); + } + } } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/execution/runners/AsyncGenericProgramRunner.java b/platform/lang-api/src/com/intellij/execution/runners/AsyncGenericProgramRunner.java index 2c868c717e63..4130b077b24b 100644 --- a/platform/lang-api/src/com/intellij/execution/runners/AsyncGenericProgramRunner.java +++ b/platform/lang-api/src/com/intellij/execution/runners/AsyncGenericProgramRunner.java @@ -60,6 +60,14 @@ public abstract class AsyncGenericProgramRunner @Nullable final Callback callback, @Nullable final RunProfileStarter starter) { ExecutionManager.getInstance(environment.getProject()).startRunProfile(new RunProfileStarter() { + @Override + public Promise executeAsync(@NotNull RunProfileState state, @NotNull ExecutionEnvironment environment) { + if (starter == null) { + return Promise.resolve(postProcess(environment, null, callback)); + } + return starter.executeAsync(state, environment).then(descriptor -> postProcess(environment, descriptor, callback)); + } + @Override public RunContentDescriptor execute(@NotNull RunProfileState state, @NotNull ExecutionEnvironment environment) throws ExecutionException { return postProcess(environment, starter == null ? null : starter.execute(state, environment), callback); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java index 95b734c0592d..3c96ca98899c 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java @@ -53,6 +53,7 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Trinity; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.AppUIUtil; import com.intellij.ui.docking.DockManager; import com.intellij.util.Alarm; import com.intellij.util.SmartList; @@ -377,63 +378,59 @@ public class ExecutionManagerImpl extends ExecutionManager implements Disposable } RunProfile profile = environment.getRunProfile(); - boolean started = false; - try { - project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarting(executor.getId(), environment); + project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarting(executor.getId(), environment); - final RunContentDescriptor descriptor = starter.execute(state, environment); - if (descriptor != null) { - final Trinity trinity = - Trinity.create(descriptor, environment.getRunnerAndConfigurationSettings(), executor); - myRunningConfigurations.add(trinity); - Disposer.register(descriptor, () -> myRunningConfigurations.remove(trinity)); - getContentManager().showRunContent(executor, descriptor, environment.getContentToReuse()); - final ProcessHandler processHandler = descriptor.getProcessHandler(); - if (processHandler != null) { - if (!processHandler.isStartNotified()) { - processHandler.startNotify(); - } - project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarted(executor.getId(), environment, processHandler); - started = true; + starter.executeAsync(state, environment).done(descriptor -> { + AppUIUtil.invokeOnEdt(() -> { + if (descriptor != null) { + final Trinity trinity = + Trinity.create(descriptor, environment.getRunnerAndConfigurationSettings(), executor); + myRunningConfigurations.add(trinity); + Disposer.register(descriptor, () -> myRunningConfigurations.remove(trinity)); + getContentManager().showRunContent(executor, descriptor, environment.getContentToReuse()); + final ProcessHandler processHandler = descriptor.getProcessHandler(); + if (processHandler != null) { + if (!processHandler.isStartNotified()) { + processHandler.startNotify(); + } + project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarted(executor.getId(), environment, processHandler); - ProcessExecutionListener listener = new ProcessExecutionListener(project, executor.getId(), environment, processHandler, descriptor); - processHandler.addProcessListener(listener); - - // Since we cannot guarantee that the listener is added before process handled is start notified, - // we have to make sure the process termination events are delivered to the clients. - // Here we check the current process state and manually deliver events, while - // the ProcessExecutionListener guarantees each such event is only delivered once - // either by this code, or by the ProcessHandler. + ProcessExecutionListener listener = new ProcessExecutionListener(project, executor.getId(), environment, processHandler, descriptor); + processHandler.addProcessListener(listener); - boolean terminating = processHandler.isProcessTerminating(); - boolean terminated = processHandler.isProcessTerminated(); - if (terminating || terminated) { - listener.processWillTerminate(new ProcessEvent(processHandler), false /*doesn't matter*/); + // Since we cannot guarantee that the listener is added before process handled is start notified, + // we have to make sure the process termination events are delivered to the clients. + // Here we check the current process state and manually deliver events, while + // the ProcessExecutionListener guarantees each such event is only delivered once + // either by this code, or by the ProcessHandler. - if (terminated) { - //noinspection ConstantConditions - int exitCode = processHandler.getExitCode(); - listener.processTerminated(new ProcessEvent(processHandler, exitCode)); + boolean terminating = processHandler.isProcessTerminating(); + boolean terminated = processHandler.isProcessTerminated(); + if (terminating || terminated) { + listener.processWillTerminate(new ProcessEvent(processHandler), false /*doesn't matter*/); + + if (terminated) { + //noinspection ConstantConditions + int exitCode = processHandler.getExitCode(); + listener.processTerminated(new ProcessEvent(processHandler, exitCode)); + } } } + environment.setContentToReuse(descriptor); } - environment.setContentToReuse(descriptor); + else { + project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(executor.getId(), environment); + } + }, o -> project.isDisposed()); + }).rejected(e -> { + if (!(e instanceof ProcessCanceledException)) { + ExecutionException error = e instanceof ExecutionException ? (ExecutionException)e : new ExecutionException(e); + ExecutionUtil.handleExecutionError(project, ExecutionManager.getInstance(project).getContentManager().getToolWindowIdByEnvironment(environment), + profile, error); } - } - catch (ProcessCanceledException e) { LOG.info(e); - } - catch (ExecutionException e) { - ExecutionUtil.handleExecutionError(project, - ExecutionManager.getInstance(project).getContentManager().getToolWindowIdByEnvironment(environment), - profile, e); - LOG.info(e); - } - finally { - if (!started) { - project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(executor.getId(), environment); - } - } + project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(executor.getId(), environment); + }); }; if (ApplicationManager.getApplication().isUnitTestMode() && !myForceCompilationInTests) {