use ActionManager#tryToExecute to simplify code (IDEA-CR-9987)
[idea/community.git] / platform / lang-impl / src / com / intellij / unscramble / AnalyzeStacktraceUtil.java
1 /*
2  * Copyright 2000-2015 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.unscramble;
18
19 import com.intellij.execution.ExecutionManager;
20 import com.intellij.execution.Executor;
21 import com.intellij.execution.executors.DefaultRunExecutor;
22 import com.intellij.execution.filters.Filter;
23 import com.intellij.execution.filters.TextConsoleBuilder;
24 import com.intellij.execution.filters.TextConsoleBuilderFactory;
25 import com.intellij.execution.impl.ConsoleViewImpl;
26 import com.intellij.execution.ui.*;
27 import com.intellij.execution.ui.actions.CloseAction;
28 import com.intellij.openapi.Disposable;
29 import com.intellij.openapi.actionSystem.*;
30 import com.intellij.openapi.actionSystem.ex.ActionUtil;
31 import com.intellij.openapi.application.ApplicationManager;
32 import com.intellij.openapi.command.CommandProcessor;
33 import com.intellij.openapi.editor.Document;
34 import com.intellij.openapi.editor.Editor;
35 import com.intellij.openapi.editor.EditorFactory;
36 import com.intellij.openapi.editor.EditorSettings;
37 import com.intellij.openapi.extensions.ExtensionPointName;
38 import com.intellij.openapi.extensions.Extensions;
39 import com.intellij.openapi.ide.CopyPasteManager;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.ui.playback.commands.ActionCommand;
42 import com.intellij.openapi.util.Disposer;
43 import com.intellij.openapi.util.text.StringUtil;
44 import com.intellij.util.ui.JBUI;
45 import com.intellij.util.ui.update.UiNotifyConnector;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
48
49 import javax.swing.*;
50 import java.awt.*;
51 import java.awt.datatransfer.DataFlavor;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.ActionListener;
54
55 /**
56  * @author yole
57  */
58 public class AnalyzeStacktraceUtil {
59   public static final ExtensionPointName<Filter> EP_NAME = ExtensionPointName.create("com.intellij.analyzeStacktraceFilter");
60
61   private AnalyzeStacktraceUtil() {
62   }
63
64   public static void printStacktrace(@NotNull ConsoleView consoleView, @NotNull String unscrambledTrace) {
65     ApplicationManager.getApplication().assertIsDispatchThread();
66     String text = unscrambledTrace + "\n";
67     String consoleText = ((ConsoleViewImpl)consoleView).getText();
68     if (!text.equals(consoleText)) {
69       consoleView.clear();
70       consoleView.print(text, ConsoleViewContentType.ERROR_OUTPUT);
71       consoleView.scrollTo(0);
72     }
73   }
74
75   @Nullable
76   public static String getTextInClipboard() {
77     return CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor);
78   }
79
80   public interface ConsoleFactory {
81     JComponent createConsoleComponent(ConsoleView consoleView, DefaultActionGroup toolbarActions);
82   }
83
84   public static void addConsole(Project project, @Nullable ConsoleFactory consoleFactory, final String tabTitle, String text) {
85     addConsole(project, consoleFactory, tabTitle, text, null);
86   }
87
88   public static RunContentDescriptor addConsole(Project project,
89                                                 @Nullable ConsoleFactory consoleFactory,
90                                                 final String tabTitle,
91                                                 String text,
92                                                 @Nullable Icon icon) {
93     final TextConsoleBuilder builder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
94     builder.filters(Extensions.getExtensions(EP_NAME, project));
95     final ConsoleView consoleView = builder.getConsole();
96
97     final DefaultActionGroup toolbarActions = new DefaultActionGroup();
98     JComponent consoleComponent = consoleFactory != null
99                                   ? consoleFactory.createConsoleComponent(consoleView, toolbarActions)
100                                   : new MyConsolePanel(consoleView, toolbarActions);
101     final RunContentDescriptor descriptor =
102       new RunContentDescriptor(consoleView, null, consoleComponent, tabTitle, icon) {
103       @Override
104       public boolean isContentReuseProhibited() {
105         return true;
106       }
107     };
108     registerReplaceAction(project, descriptor, consoleView, text);
109
110     final Executor executor = DefaultRunExecutor.getRunExecutorInstance();
111     for (AnAction action: consoleView.createConsoleActions()) {
112       toolbarActions.add(action);
113     }
114     final ConsoleViewImpl console = (ConsoleViewImpl)consoleView;
115     console.getEditor().getSettings().setCaretRowShown(true);
116     toolbarActions.add(new AnnotateStackTraceAction(console.getEditor(), console.getHyperlinks()));
117     toolbarActions.add(new CloseAction(executor, descriptor, project));
118     ExecutionManager.getInstance(project).getContentManager().showRunContent(executor, descriptor);
119     consoleView.allowHeavyFilters();
120     if (consoleFactory == null) {
121       printStacktrace(consoleView, text);
122     }
123     return descriptor;
124   }
125
126   /**
127    * Regular "Replace" editor action is unavailable for console editors (see com.intellij.openapi.editor.actions.IncrementalFindAction.Handler#isEnabled).
128    * However, it can be convenient to be able to replace user-specific paths with local paths
129    * if stacktrace links involves absolute paths (e.g. Node.js stack-traces contain absolute paths).
130    *
131    * @param consoleView
132    */
133   private static void registerReplaceAction(@NotNull Project project,
134                                             @NotNull RunContentDescriptor descriptor,
135                                             @NotNull ConsoleView consoleView,
136                                             @NotNull String stacktrace) {
137     AnAction replaceAction = ActionManager.getInstance().getAction(IdeActions.ACTION_REPLACE);
138     if (replaceAction == null) {
139       return;
140     }
141     ActionUtil.registerForEveryKeyboardShortcut(consoleView.getComponent(), new ActionListener() {
142       @Override
143       public void actionPerformed(ActionEvent e) {
144         AnalyzeStacktraceDialog dialog = new AnalyzeStacktraceDialog(project) {
145           @Override
146           protected JComponent createCenterPanel() {
147             JComponent result = super.createCenterPanel();
148             myEditorPanel.setText(stacktrace);
149             UiNotifyConnector.doWhenFirstShown(myEditorPanel, new Runnable() {
150               @Override
151               public void run() {
152                 ActionManager.getInstance().tryToExecute(replaceAction,
153                                                          ActionCommand.getInputEvent(IdeActions.ACTION_REPLACE),
154                                                          myEditorPanel,
155                                                          ActionPlaces.UNKNOWN,
156                                                          false);
157               }
158             });
159             return result;
160           }
161
162           @Override
163           protected void doOKAction() {
164             super.doOKAction();
165             RunContentManager contentManager = ExecutionManager.getInstance(project).getContentManager();
166             contentManager.removeRunContent(DefaultRunExecutor.getRunExecutorInstance(), descriptor);
167           }
168         };
169         dialog.show();
170       }
171     }, replaceAction.getShortcutSet());
172   }
173
174   private static final class MyConsolePanel extends JPanel {
175     public MyConsolePanel(ExecutionConsole consoleView, ActionGroup toolbarActions) {
176       super(new BorderLayout());
177       JPanel toolbarPanel = new JPanel(new BorderLayout());
178       toolbarPanel.add(ActionManager.getInstance()
179                          .createActionToolbar(ActionPlaces.ANALYZE_STACKTRACE_PANEL_TOOLBAR, toolbarActions, false)
180                          .getComponent());
181       add(toolbarPanel, BorderLayout.WEST);
182       add(consoleView.getComponent(), BorderLayout.CENTER);
183     }
184   }
185
186   public static StacktraceEditorPanel createEditorPanel(Project project, @NotNull Disposable parentDisposable) {
187     EditorFactory editorFactory = EditorFactory.getInstance();
188     Document document = editorFactory.createDocument("");
189     Editor editor = editorFactory.createEditor(document, project);
190     EditorSettings settings = editor.getSettings();
191     settings.setFoldingOutlineShown(false);
192     settings.setLineMarkerAreaShown(false);
193     settings.setIndentGuidesShown(false);
194     settings.setLineNumbersShown(false);
195     settings.setRightMarginShown(false);
196
197     StacktraceEditorPanel editorPanel = new StacktraceEditorPanel(project, editor);
198     editorPanel.setPreferredSize(JBUI.size(600, 400));
199     Disposer.register(parentDisposable, editorPanel);
200     return editorPanel;
201   }
202
203   public static final class StacktraceEditorPanel extends JPanel implements DataProvider, Disposable {
204     private final Project myProject;
205     private final Editor myEditor;
206
207     public StacktraceEditorPanel(Project project, Editor editor) {
208       super(new BorderLayout());
209       myProject = project;
210       myEditor = editor;
211       add(myEditor.getComponent());
212     }
213
214     @Override
215     public Object getData(String dataId) {
216       if (CommonDataKeys.EDITOR.is(dataId)) {
217         return myEditor;
218       }
219       return null;
220     }
221
222     public Editor getEditor() {
223       return myEditor;
224     }
225
226     public final void setText(@NotNull final String text) {
227       Runnable runnable = new Runnable() {
228         @Override
229         public void run() {
230           ApplicationManager.getApplication().runWriteAction(new Runnable() {
231             @Override
232             public void run() {
233               final Document document = myEditor.getDocument();
234               document.replaceString(0, document.getTextLength(), StringUtil.convertLineSeparators(text));
235             }
236           });
237         }
238       };
239       CommandProcessor.getInstance().executeCommand(myProject, runnable, "", this);
240     }
241
242     public void pasteTextFromClipboard() {
243       String text = getTextInClipboard();
244       if (text != null) {
245         setText(text);
246       }
247
248     }
249
250     @Override
251     public void dispose() {
252       EditorFactory.getInstance().releaseEditor(myEditor);
253     }
254
255     public String getText() {
256       return myEditor.getDocument().getText();
257     }
258
259     public JComponent getEditorComponent() {
260       return myEditor.getContentComponent();
261     }
262   }
263 }