Python execute in console: added selection of the console in case of more than one.
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / ExecutionHelper.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.execution;
18
19 import com.google.common.collect.Lists;
20 import com.intellij.execution.executors.DefaultRunExecutor;
21 import com.intellij.execution.process.OSProcessHandler;
22 import com.intellij.execution.process.ProcessHandler;
23 import com.intellij.execution.ui.RunContentDescriptor;
24 import com.intellij.execution.ui.RunContentManagerImpl;
25 import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
26 import com.intellij.ide.ui.ListCellRendererWrapper;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.command.CommandProcessor;
29 import com.intellij.openapi.components.ServiceManager;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.editor.Editor;
32 import com.intellij.openapi.progress.ProgressIndicator;
33 import com.intellij.openapi.progress.ProgressManager;
34 import com.intellij.openapi.progress.Task;
35 import com.intellij.openapi.project.Project;
36 import com.intellij.openapi.ui.Messages;
37 import com.intellij.openapi.ui.popup.PopupChooserBuilder;
38 import com.intellij.openapi.util.Disposer;
39 import com.intellij.openapi.util.text.StringUtil;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.openapi.wm.ToolWindow;
42 import com.intellij.openapi.wm.ToolWindowId;
43 import com.intellij.openapi.wm.ToolWindowManager;
44 import com.intellij.ui.components.JBList;
45 import com.intellij.ui.content.Content;
46 import com.intellij.ui.content.ContentFactory;
47 import com.intellij.ui.content.MessageView;
48 import com.intellij.util.Consumer;
49 import com.intellij.util.Function;
50 import com.intellij.util.NotNullFunction;
51 import com.intellij.util.concurrency.Semaphore;
52 import com.intellij.util.ui.ErrorTreeView;
53 import com.intellij.util.ui.MessageCategory;
54 import org.jetbrains.annotations.NotNull;
55 import org.jetbrains.annotations.Nullable;
56
57 import javax.swing.*;
58 import java.util.ArrayList;
59 import java.util.Collection;
60 import java.util.Collections;
61 import java.util.List;
62
63 /**
64  * Created by IntelliJ IDEA.
65  *
66  * @author: Roman Chernyatchik
67  * @date: Oct 4, 2007
68  */
69 public class ExecutionHelper {
70   private static final Logger LOG = Logger.getInstance(ExecutionHelper.class.getName());
71
72   private ExecutionHelper() {
73   }
74
75   public static void showErrors(@NotNull final Project myProject,
76                                 @NotNull final List<Exception> exceptionList,
77                                 @NotNull final String tabDisplayName,
78                                 @Nullable final VirtualFile file) {
79     if (ApplicationManager.getApplication().isUnitTestMode() && !exceptionList.isEmpty()) {
80       throw new RuntimeException(exceptionList.get(0));
81     }
82     ApplicationManager.getApplication().invokeLater(new Runnable() {
83       public void run() {
84         if (myProject.isDisposed()) return;
85         if (exceptionList.isEmpty()) {
86           removeContents(null, myProject, tabDisplayName);
87           return;
88         }
89
90         final RailsErrorViewPanel errorTreeView = new RailsErrorViewPanel(myProject);
91         try {
92           openMessagesView(errorTreeView, myProject, tabDisplayName);
93         }
94         catch (NullPointerException e) {
95           final StringBuilder builder = new StringBuilder();
96           builder.append("Exceptions occured:");
97           for (final Exception exception : exceptionList) {
98             builder.append("\n");
99             builder.append(exception.getMessage());
100           }
101           Messages.showErrorDialog(builder.toString(), "Execution Error");
102           return;
103         }
104         for (final Exception exception : exceptionList) {
105           String[] messages = StringUtil.splitByLines(exception.getMessage());
106           if (messages.length == 0) messages = new String[]{"Unknown Error"};
107           errorTreeView.addMessage(MessageCategory.ERROR, messages, file, -1, -1, null);
108         }
109
110         ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.MESSAGES_WINDOW).activate(null);
111       }
112     });
113   }
114
115   private static void openMessagesView(@NotNull final RailsErrorViewPanel errorTreeView,
116                                        @NotNull final Project myProject,
117                                        @NotNull final String tabDisplayName) {
118     CommandProcessor commandProcessor = CommandProcessor.getInstance();
119     commandProcessor.executeCommand(myProject, new Runnable() {
120       public void run() {
121         final MessageView messageView = ServiceManager.getService(myProject, MessageView.class);
122         final Content content = ContentFactory.SERVICE.getInstance().createContent(errorTreeView, tabDisplayName, true);
123         messageView.getContentManager().addContent(content);
124         Disposer.register(content, errorTreeView);
125         messageView.getContentManager().setSelectedContent(content);
126         removeContents(content, myProject, tabDisplayName);
127       }
128     }, "Open message view", null);
129   }
130
131   private static void removeContents(@Nullable final Content notToRemove,
132                                      @NotNull final Project myProject,
133                                      @NotNull final String tabDisplayName) {
134     MessageView messageView = ServiceManager.getService(myProject, MessageView.class);
135     Content[] contents = messageView.getContentManager().getContents();
136     for (Content content : contents) {
137       LOG.assertTrue(content != null);
138       if (content.isPinned()) continue;
139       if (tabDisplayName.equals(content.getDisplayName()) && content != notToRemove) {
140         ErrorTreeView listErrorView = (ErrorTreeView)content.getComponent();
141         if (listErrorView != null) {
142           if (messageView.getContentManager().removeContent(content, true)) {
143             content.release();
144           }
145         }
146       }
147     }
148   }
149
150   public static Collection<RunContentDescriptor> findRunningConsoleByCmdLine(final Project project,
151                                                                              @NotNull final NotNullFunction<String, Boolean> cmdLineMatcher) {
152     return findRunningConsole(project, new NotNullFunction<RunContentDescriptor, Boolean>() {
153       @NotNull
154       @Override
155       public Boolean fun(RunContentDescriptor selectedContent) {
156         final ProcessHandler processHandler = selectedContent.getProcessHandler();
157         if (processHandler instanceof OSProcessHandler && !processHandler.isProcessTerminated()) {
158           final String commandLine = ((OSProcessHandler)processHandler).getCommandLine();
159           return cmdLineMatcher.fun(commandLine);
160         }
161         return false;
162       }
163     });
164   }
165
166   public static Collection<RunContentDescriptor> findRunningConsoleByTitle(final Project project,
167                                                                            @NotNull final NotNullFunction<String, Boolean> titleMatcher) {
168     return findRunningConsole(project, new NotNullFunction<RunContentDescriptor, Boolean>() {
169       @NotNull
170       @Override
171       public Boolean fun(RunContentDescriptor selectedContent) {
172         return titleMatcher.fun(selectedContent.getDisplayName());
173       }
174     });
175   }
176
177   public static Collection<RunContentDescriptor> findRunningConsole(final Project project,
178                                                                     @NotNull final NotNullFunction<RunContentDescriptor, Boolean> descriptorMatcher) {
179     final ExecutionManager executionManager = ExecutionManager.getInstance(project);
180     final ToolWindow runToolWindow =
181       ToolWindowManager.getInstance(project).getToolWindow(DefaultRunExecutor.getRunExecutorInstance().getId());
182     if (runToolWindow != null && runToolWindow.isVisible()) {
183       final RunContentDescriptor selectedContent = executionManager.getContentManager().getSelectedContent();
184       if (selectedContent != null) {
185         if (descriptorMatcher.fun(selectedContent)) {
186           return Collections.singletonList(selectedContent);
187         }
188       }
189     }
190
191     final ArrayList<RunContentDescriptor> result = Lists.newArrayList();
192     final RunContentDescriptor[] runContentDescriptors = ((RunContentManagerImpl)executionManager.getContentManager()).getAllDescriptors();
193     for (RunContentDescriptor runContentDescriptor : runContentDescriptors) {
194       if (descriptorMatcher.fun(runContentDescriptor)) {
195         result.add(runContentDescriptor);
196       }
197     }
198     return result;
199   }
200
201   public static List<RunContentDescriptor> collectConsolesByDisplayName(final Project project,
202                                                                         @NotNull NotNullFunction<String, Boolean> titleMatcher) {
203     List<RunContentDescriptor> result = Lists.newArrayList();
204     final ExecutionManager executionManager = ExecutionManager.getInstance(project);
205     final RunContentDescriptor[] runContentDescriptors = ((RunContentManagerImpl)executionManager.getContentManager()).getAllDescriptors();
206     for (RunContentDescriptor runContentDescriptor : runContentDescriptors) {
207       if (titleMatcher.fun(runContentDescriptor.getDisplayName())) {
208         result.add(runContentDescriptor);
209       }
210     }
211     return result;
212   }
213
214   public static void selectContentDescriptor(final @NotNull Editor editor,
215                                                              @NotNull Collection<RunContentDescriptor> consoles,
216                                                              String selectDialogTitle, final Consumer<RunContentDescriptor> descriptorConsumer) {
217     if (consoles.size() == 1) {
218       RunContentDescriptor descriptor = consoles.iterator().next();
219       descriptorConsumer.consume(descriptor);
220       descriptorToFront(editor, descriptor);
221     }
222     else if (consoles.size() > 1) {
223       final JList list = new JBList(consoles);
224       final Icon icon = DefaultRunExecutor.getRunExecutorInstance().getIcon();
225       list.setCellRenderer(new ListCellRendererWrapper<RunContentDescriptor>(list.getCellRenderer()) {
226         @Override
227         public void customize(final JList list,
228                               final RunContentDescriptor value,
229                               final int index,
230                               final boolean selected,
231                               final boolean hasFocus) {
232           setText(value.getDisplayName());
233           setIcon(icon);
234         }
235       });
236
237       final PopupChooserBuilder builder = new PopupChooserBuilder(list);
238       builder.setTitle(selectDialogTitle);
239
240       builder.setItemChoosenCallback(new Runnable() {
241         @Override
242         public void run() {
243           final Object selectedValue = list.getSelectedValue();
244           if (selectedValue instanceof RunContentDescriptor) {
245             RunContentDescriptor descriptor = (RunContentDescriptor)selectedValue;
246             descriptorConsumer.consume(descriptor);
247             descriptorToFront(editor, descriptor);
248           }
249         }
250       }).createPopup().showInBestPositionFor(editor);
251     }
252   }
253
254   private static void descriptorToFront(Editor editor, RunContentDescriptor descriptor) {
255     ExecutionManager.getInstance(editor.getProject()).getContentManager()
256       .toFrontRunContent(DefaultRunExecutor.getRunExecutorInstance(), descriptor);
257   }
258
259   public static class RailsErrorViewPanel extends NewErrorTreeViewPanel {
260     public RailsErrorViewPanel(final Project project) {
261       super(project, "reference.toolWindows.messages");
262     }
263
264     protected boolean canHideWarnings() {
265       return false;
266     }
267   }
268
269
270   public static void executeExternalProcess(@Nullable final Project myProject,
271                                             @NotNull final OSProcessHandler processHandler,
272                                             @NotNull final ExecutionMode mode) {
273     final String title = mode.getTitle() != null ? mode.getTitle() : "Please wait...";
274     assert title != null;
275
276     final Runnable process;
277     if (mode.cancelable()) {
278       process = createCancelableExecutionProcess(processHandler, mode.shouldCancelFun());
279     }
280     else {
281       if (mode.getTimeout() <= 0) {
282         process = new Runnable() {
283           public void run() {
284             processHandler.waitFor();
285           }
286         };
287       }
288       else {
289         process = createTimelimitedExecutionProcess(processHandler, mode.getTimeout());
290       }
291     }
292     if (mode.withModalProgress()) {
293       ProgressManager.getInstance().runProcessWithProgressSynchronously(process, title, mode.cancelable(), myProject,
294                                                                         mode.getProgressParentComponent());
295     }
296     else if (mode.inBackGround()) {
297       final Task task = new Task.Backgroundable(myProject, title, mode.cancelable()) {
298         public void run(@NotNull final ProgressIndicator indicator) {
299           process.run();
300         }
301       };
302       ProgressManager.getInstance().run(task);
303     }
304     else {
305       final String title2 = mode.getTitle2();
306       final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
307       if (indicator != null && title2 != null) {
308         indicator.setText2(title2);
309       }
310       process.run();
311     }
312   }
313
314   private static Runnable createCancelableExecutionProcess(final ProcessHandler processHandler,
315                                                            final Function<Object, Boolean> cancelableFun) {
316     return new Runnable() {
317       private ProgressIndicator myProgressIndicator;
318       private final Semaphore mySemaphore = new Semaphore();
319
320       private final Runnable myWaitThread = new Runnable() {
321         public void run() {
322           try {
323             processHandler.waitFor();
324           }
325           finally {
326             mySemaphore.up();
327           }
328         }
329       };
330
331       private final Runnable myCancelListener = new Runnable() {
332         public void run() {
333           for (; ;) {
334             if ((myProgressIndicator != null && (myProgressIndicator.isCanceled()
335                                                  || !myProgressIndicator.isRunning()))
336                 || (cancelableFun != null && cancelableFun.fun(null).booleanValue())
337                 || processHandler.isProcessTerminated()) {
338
339               if (!processHandler.isProcessTerminated()) {
340                 try {
341                   processHandler.destroyProcess();
342                 }
343                 finally {
344                   mySemaphore.up();
345                 }
346               }
347               break;
348             }
349             try {
350               synchronized (this) {
351                 wait(1000);
352               }
353             }
354             catch (InterruptedException e) {
355               //Do nothing
356             }
357           }
358         }
359       };
360
361       public void run() {
362         myProgressIndicator = ProgressManager.getInstance().getProgressIndicator();
363         if (myProgressIndicator != null && StringUtil.isEmpty(myProgressIndicator.getText())) {
364           myProgressIndicator.setText("Please wait...");
365         }
366
367         LOG.assertTrue(myProgressIndicator != null || cancelableFun != null,
368                        "Cancelable process must have an opportunity to be canceled!");
369         mySemaphore.down();
370         ApplicationManager.getApplication().executeOnPooledThread(myWaitThread);
371         ApplicationManager.getApplication().executeOnPooledThread(myCancelListener);
372
373         mySemaphore.waitFor();
374       }
375     };
376   }
377
378   private static Runnable createTimelimitedExecutionProcess(final OSProcessHandler processHandler,
379                                                             final int timeout) {
380     return new Runnable() {
381       private final Semaphore mySemaphore = new Semaphore();
382
383       private final Runnable myProcessThread = new Runnable() {
384         public void run() {
385           try {
386             final boolean finished = processHandler.waitFor(1000 * timeout);
387             if (!finished) {
388               LOG.error("Timeout (" + timeout + " sec) on executing: " + processHandler.getCommandLine());
389               processHandler.destroyProcess();
390             }
391           }
392           finally {
393             mySemaphore.up();
394           }
395         }
396       };
397
398       public void run() {
399         mySemaphore.down();
400         ApplicationManager.getApplication().executeOnPooledThread(myProcessThread);
401
402         mySemaphore.waitFor();
403       }
404     };
405   }
406 }