package com.jetbrains.lang.dart.ide.runner.server; import com.intellij.execution.ExecutionException; import com.intellij.execution.Executor; import com.intellij.execution.configurations.CommandLineState; import com.intellij.execution.configurations.CommandLineTokenizer; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.GeneralCommandLine.ParentEnvironmentType; import com.intellij.execution.configurations.RuntimeConfigurationError; import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilderImpl; import com.intellij.execution.filters.UrlFilter; import com.intellij.execution.process.*; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.ui.ConsoleView; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.Separator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import com.intellij.util.net.NetUtils; import com.jetbrains.lang.dart.DartBundle; import com.jetbrains.lang.dart.coverage.DartCoverageProgramRunner; import com.jetbrains.lang.dart.ide.runner.DartConsoleFilter; import com.jetbrains.lang.dart.ide.runner.DartExecutionHelper; import com.jetbrains.lang.dart.ide.runner.DartRelativePathsConsoleFilter; import com.jetbrains.lang.dart.ide.runner.base.DartRunConfiguration; import com.jetbrains.lang.dart.ide.runner.client.DartiumUtil; import com.jetbrains.lang.dart.sdk.DartSdk; import com.jetbrains.lang.dart.sdk.DartSdkUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.*; public class DartCommandLineRunningState extends CommandLineState { protected final @NotNull DartCommandLineRunnerParameters myRunnerParameters; private int myObservatoryPort = -1; private Collection<Consumer<String>> myObservatoryUrlConsumers = new ArrayList<>(); public DartCommandLineRunningState(final @NotNull ExecutionEnvironment env) throws ExecutionException { super(env); myRunnerParameters = ((DartRunConfiguration)env.getRunProfile()).getRunnerParameters().clone(); final Project project = env.getProject(); try { myRunnerParameters.check(project); } catch (RuntimeConfigurationError e) { throw new ExecutionException(e); } final TextConsoleBuilder builder = getConsoleBuilder(); if (builder instanceof TextConsoleBuilderImpl) { ((TextConsoleBuilderImpl)builder).setUsePredefinedMessageFilter(false); } try { builder.addFilter(new DartConsoleFilter(project, myRunnerParameters.getDartFileOrDirectory())); builder.addFilter(new DartRelativePathsConsoleFilter(project, myRunnerParameters.computeProcessWorkingDirectory(project))); builder.addFilter(new UrlFilter()); } catch (RuntimeConfigurationError e) { /* can't happen because already checked */} } public void addObservatoryUrlConsumer(@NotNull final Consumer<String> consumer) { myObservatoryUrlConsumers.add(consumer); } @NotNull @Override protected AnAction[] createActions(final ConsoleView console, final ProcessHandler processHandler, final Executor executor) { // These actions are effectively added only to the Run tool window. For Debug see DartCommandLineDebugProcess.registerAdditionalActions() final List<AnAction> actions = new ArrayList<>(Arrays.asList(super.createActions(console, processHandler, executor))); addObservatoryActions(actions, processHandler); return actions.toArray(new AnAction[actions.size()]); } protected void addObservatoryActions(List<AnAction> actions, final ProcessHandler processHandler) { actions.add(new Separator()); final OpenDartObservatoryUrlAction openObservatoryAction = new OpenDartObservatoryUrlAction(null, () -> !processHandler.isProcessTerminated()); addObservatoryUrlConsumer(openObservatoryAction::setUrl); actions.add(openObservatoryAction); } @NotNull @Override protected ProcessHandler startProcess() throws ExecutionException { return doStartProcess(null); } protected ProcessHandler doStartProcess(final @Nullable String overriddenMainFilePath) throws ExecutionException { final GeneralCommandLine commandLine = createCommandLine(overriddenMainFilePath); // Workaround for "Observatory listening on ..." message that is concatenated (without line break) with the message following it final OSProcessHandler processHandler = new ColoredProcessHandler(commandLine) { @Override public void coloredTextAvailable(@NotNull String text, @NotNull Key attributes) { if (text.startsWith(DartConsoleFilter.OBSERVATORY_LISTENING_ON)) { text += "\n"; } super.coloredTextAvailable(text, attributes); } }; processHandler.addProcessListener(new ProcessAdapter() { @Override public void onTextAvailable(final ProcessEvent event, final Key outputType) { final String prefix = DartConsoleFilter.OBSERVATORY_LISTENING_ON + "http://"; final String text = event.getText().trim(); if (text.startsWith(prefix)) { processHandler.removeProcessListener(this); final String url = "http://" + text.substring(prefix.length()); for (Consumer<String> consumer : myObservatoryUrlConsumers) { consumer.consume(url); } } } }); // Check for and display any analysis errors when we launch a Dart app. final Project project = getEnvironment().getProject(); try { final DartRunConfiguration dartRunConfiguration = (DartRunConfiguration)getEnvironment().getRunProfile(); final VirtualFile launchFile = dartRunConfiguration.getRunnerParameters().getDartFileOrDirectory(); String launchTitle = "Analysis issues with " + dartRunConfiguration.getName(); DartExecutionHelper.displayIssues(project, launchFile, launchTitle, dartRunConfiguration.getIcon()); } catch (RuntimeConfigurationError error) { DartExecutionHelper.clearIssueNotifications(project); } ProcessTerminatedListener.attach(processHandler, getEnvironment().getProject()); return processHandler; } private GeneralCommandLine createCommandLine(@Nullable final String overriddenMainFilePath) throws ExecutionException { final DartSdk sdk = DartSdk.getDartSdk(getEnvironment().getProject()); if (sdk == null) { throw new ExecutionException(DartBundle.message("dart.sdk.is.not.configured")); } final GeneralCommandLine commandLine = new GeneralCommandLine() .withWorkDirectory(myRunnerParameters.computeProcessWorkingDirectory(getEnvironment().getProject())); commandLine.setCharset(CharsetToolkit.UTF8_CHARSET); commandLine.setExePath(FileUtil.toSystemDependentName(DartSdkUtil.getDartExePath(sdk))); commandLine.getEnvironment().putAll(myRunnerParameters.getEnvs()); commandLine .withParentEnvironmentType(myRunnerParameters.isIncludeParentEnvs() ? ParentEnvironmentType.CONSOLE : ParentEnvironmentType.NONE); setupParameters(commandLine, overriddenMainFilePath); return commandLine; } private void setupParameters(@NotNull final GeneralCommandLine commandLine, @Nullable final String overriddenMainFilePath) throws ExecutionException { int customObservatoryPort = -1; final String vmOptions = myRunnerParameters.getVMOptions(); if (vmOptions != null) { final StringTokenizer vmOptionsTokenizer = new CommandLineTokenizer(vmOptions); while (vmOptionsTokenizer.hasMoreTokens()) { final String vmOption = vmOptionsTokenizer.nextToken(); commandLine.addParameter(vmOption); try { if (vmOption.equals("--enable-vm-service") || vmOption.equals("--observe")) { customObservatoryPort = 8181; // default port, see https://www.dartlang.org/tools/dart-vm/ } else if (vmOption.startsWith("--enable-vm-service:")) { customObservatoryPort = parseIntBeforeSlash(vmOption.substring("--enable-vm-service:".length())); } else if (vmOption.startsWith("--observe:")) { customObservatoryPort = parseIntBeforeSlash(vmOption.substring("--observe:".length())); } } catch (NumberFormatException ignore) {/**/} } } if (myRunnerParameters.isCheckedMode()) { commandLine.addParameter(DartiumUtil.CHECKED_MODE_OPTION); } final VirtualFile dartFile; try { dartFile = myRunnerParameters.getDartFileOrDirectory(); } catch (RuntimeConfigurationError e) { throw new ExecutionException(e); } if (DefaultDebugExecutor.EXECUTOR_ID.equals(getEnvironment().getExecutor().getId())) { commandLine.addParameter("--pause_isolates_on_start"); } if (customObservatoryPort > 0) { myObservatoryPort = customObservatoryPort; } else { try { myObservatoryPort = NetUtils.findAvailableSocketPort(); } catch (IOException e) { throw new ExecutionException(e); } commandLine.addParameter("--enable-vm-service:" + myObservatoryPort); if (getEnvironment().getRunner() instanceof DartCoverageProgramRunner) { commandLine.addParameter("--pause-isolates-on-exit"); } } commandLine.addParameter(FileUtil.toSystemDependentName(overriddenMainFilePath == null ? dartFile.getPath() : overriddenMainFilePath)); final String arguments = myRunnerParameters.getArguments(); if (arguments != null) { StringTokenizer argumentsTokenizer = new CommandLineTokenizer(arguments); while (argumentsTokenizer.hasMoreTokens()) { commandLine.addParameter(argumentsTokenizer.nextToken()); } } } private static int parseIntBeforeSlash(@NotNull final String s) throws NumberFormatException { // "5858" or "5858/0.0.0.0" final int index = s.indexOf('/'); return Integer.parseInt(index > 0 ? s.substring(0, index) : s); } public int getObservatoryPort() { return myObservatoryPort; } }