Merge remote-tracking branch 'origin/master'
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / console / RunIdeConsoleAction.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 package com.intellij.execution.console;
17
18 import com.intellij.execution.ExecutionManager;
19 import com.intellij.execution.Executor;
20 import com.intellij.execution.executors.DefaultRunExecutor;
21 import com.intellij.execution.filters.TextConsoleBuilderFactory;
22 import com.intellij.execution.ui.ConsoleView;
23 import com.intellij.execution.ui.ConsoleViewContentType;
24 import com.intellij.execution.ui.RunContentDescriptor;
25 import com.intellij.execution.ui.actions.CloseAction;
26 import com.intellij.ide.scratch.ScratchFileService;
27 import com.intellij.openapi.actionSystem.*;
28 import com.intellij.openapi.editor.Editor;
29 import com.intellij.openapi.editor.ex.util.EditorUtil;
30 import com.intellij.openapi.fileEditor.FileEditor;
31 import com.intellij.openapi.fileEditor.FileEditorManager;
32 import com.intellij.openapi.fileEditor.TextEditor;
33 import com.intellij.openapi.project.DumbAwareAction;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.ui.popup.JBPopupFactory;
36 import com.intellij.openapi.util.Disposer;
37 import com.intellij.openapi.util.Key;
38 import com.intellij.openapi.util.TextRange;
39 import com.intellij.openapi.vfs.VirtualFile;
40 import com.intellij.psi.PsiFile;
41 import com.intellij.psi.PsiManager;
42 import com.intellij.util.NotNullFunction;
43 import com.intellij.util.PathUtil;
44 import com.intellij.util.containers.ContainerUtil;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47
48 import javax.script.ScriptEngine;
49 import javax.script.ScriptEngineFactory;
50 import javax.script.ScriptEngineManager;
51 import javax.swing.*;
52 import java.awt.*;
53 import java.io.IOException;
54 import java.lang.ref.WeakReference;
55 import java.util.List;
56 import java.util.Map;
57
58 /**
59  * @author gregsh
60  */
61 public class RunIdeConsoleAction extends DumbAwareAction {
62
63   private static final String DEFAULT_FILE_NAME = "ide-scripting";
64   private static final Key<WeakReference<ConsoleView>> CONSOLE_VIEW_KEY = Key.create("CONSOLE_VIEW_KEY");
65   private static final Key<ConsoleHistoryController> HISTORY_CONTROLLER_KEY = Key.create("HISTORY_CONTROLLER_KEY");
66
67   private static final Map<String, ScriptEngineFactory> ourEngines = ContainerUtil.newLinkedHashMap();
68
69   static {
70     for (ScriptEngineFactory factory : new ScriptEngineManager().getEngineFactories()) {
71       ourEngines.put(factory.getLanguageName(), factory);
72     }
73   }
74
75   @Override
76   public void update(AnActionEvent e) {
77     boolean enabled = !ourEngines.isEmpty() && e.getProject() != null;
78     e.getPresentation().setEnabledAndVisible(enabled);
79   }
80
81   @Override
82   public void actionPerformed(AnActionEvent e) {
83     if (ourEngines.size() == 1) {
84       runConsole(e, ourEngines.values().iterator().next());
85     }
86     else {
87       DefaultActionGroup actions = new DefaultActionGroup(
88         ContainerUtil.map(ourEngines.values(), new NotNullFunction<ScriptEngineFactory, AnAction>() {
89           @NotNull
90           @Override
91           public AnAction fun(final ScriptEngineFactory engine) {
92             return new AnAction(engine.getLanguageName()) {
93               @Override
94               public void actionPerformed(@NotNull AnActionEvent e) {
95                 runConsole(e, engine);
96               }
97             };
98           }
99         })
100       );
101       JBPopupFactory.getInstance().createActionGroupPopup("Script Engine", actions, e.getDataContext(), JBPopupFactory.ActionSelectionAid.NUMBERING, false).
102         showInBestPositionFor(e.getDataContext());
103     }
104   }
105
106   protected void runConsole(@NotNull AnActionEvent e, @NotNull ScriptEngineFactory engine) {
107     Project project = e.getProject();
108     if (project == null) return;
109     List<String> extensions = engine.getExtensions();
110     try {
111       String pathName = PathUtil.makeFileName(DEFAULT_FILE_NAME, ContainerUtil.getFirstItem(extensions));
112       VirtualFile virtualFile = IdeConsoleRootType.getInstance().findFile(project, pathName, ScratchFileService.Option.create_if_missing);
113       if (virtualFile != null) {
114         FileEditorManager.getInstance(project).openFile(virtualFile, true);
115       }
116     }
117     catch (IOException ignored) {
118     }
119   }
120
121   public static void configureConsole(@NotNull VirtualFile file, @NotNull FileEditorManager source) {
122     ScriptEngine engine = findScriptEngine(file);
123     if (engine == null) return;
124     //new ConsoleHistoryController(IdeConsoleRootType.getInstance(), engine.getFactory().getLanguageName())
125     MyRunAction runAction = new MyRunAction(engine);
126     for (FileEditor fileEditor : source.getEditors(file)) {
127       if (!(fileEditor instanceof TextEditor)) continue;
128       Editor editor = ((TextEditor)fileEditor).getEditor();
129       runAction.registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, editor.getComponent());
130     }
131   }
132
133   @Nullable
134   private static ScriptEngine findScriptEngine(@NotNull VirtualFile file) {
135     for (ScriptEngineFactory factory : ourEngines.values()) {
136       if (factory.getExtensions().contains(file.getExtension())) {
137         return factory.getScriptEngine();
138       }
139     }
140     return null;
141   }
142
143   private static void executeQuery(@NotNull Project project,
144                                    @NotNull VirtualFile file,
145                                    @NotNull Editor editor,
146                                    @NotNull ScriptEngine engine) {
147     TextRange selectedRange = EditorUtil.getSelectionInAnyMode(editor);
148     if (selectedRange.getLength() == 0) return;
149     String command = editor.getDocument().getText(selectedRange);
150     ConsoleView consoleView = getConsoleView(project, file, engine);
151     try {
152       // todo
153       //myHistoryController.getModel().addToHistory(command);
154       consoleView.print("> " + command, ConsoleViewContentType.USER_INPUT);
155       consoleView.print("\n", ConsoleViewContentType.USER_INPUT);
156       Object o = engine.eval(command);
157       String string = o == null ? "nil" : String.valueOf(o);
158       consoleView.print(string, ConsoleViewContentType.NORMAL_OUTPUT);
159       consoleView.print("\n", ConsoleViewContentType.NORMAL_OUTPUT);
160     }
161     catch (Exception e) {
162       consoleView.print(e.getMessage(), ConsoleViewContentType.ERROR_OUTPUT);
163       //consoleView.print(ExceptionUtil.getThrowableText(e), ConsoleViewContentType.ERROR_OUTPUT);
164       consoleView.print("\n", ConsoleViewContentType.ERROR_OUTPUT);
165     }
166   }
167
168   @NotNull
169   private static ConsoleView getConsoleView(@NotNull Project project, @NotNull VirtualFile file, @NotNull ScriptEngine engine) {
170     PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
171     WeakReference<ConsoleView> ref = psiFile == null ? null : psiFile.getCopyableUserData(CONSOLE_VIEW_KEY);
172     ConsoleView existing = ref == null ? null : ref.get();
173     if (existing != null && !Disposer.isDisposed(existing)) return existing;
174     ConsoleView consoleView = TextConsoleBuilderFactory.getInstance().createBuilder(project).getConsole();
175     if (psiFile != null) psiFile.putCopyableUserData(CONSOLE_VIEW_KEY, new WeakReference<ConsoleView>(consoleView));
176     DefaultActionGroup toolbarActions = new DefaultActionGroup();
177     JComponent consoleComponent = new JPanel(new BorderLayout());
178     consoleComponent.add(consoleView.getComponent(), BorderLayout.CENTER);
179     consoleComponent.add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions, false).getComponent(),
180                          BorderLayout.WEST);
181     final RunContentDescriptor descriptor = new RunContentDescriptor(consoleView, null, consoleComponent, file.getName()) {
182       @Override
183       public boolean isContentReuseProhibited() {
184         return true;
185       }
186     };
187
188     final Executor executor = DefaultRunExecutor.getRunExecutorInstance();
189     //toolbarActions.add(new DumbAwareAction("Rerun", null, AllIcons.Actions.Rerun) {
190     //  @Override
191     //  public void update(@NotNull AnActionEvent e) {
192     //    ProgressIndicator indicator = indicatorRef.get();
193     //    e.getPresentation().setEnabled(file.isValid() && (indicator == null || !indicator.isRunning()));
194     //  }
195     //
196     //  @Override
197     //  public void actionPerformed(@NotNull AnActionEvent e) {
198     //    consoleView.clear();
199     //    rerunRunnable.run();
200     //  }
201     //});
202     toolbarActions.add(new CloseAction(executor, descriptor, project));
203     for (AnAction action : consoleView.createConsoleActions()) {
204       toolbarActions.add(action);
205     }
206     ExecutionManager.getInstance(project).getContentManager().showRunContent(executor, descriptor);
207     return consoleView;
208   }
209
210
211   private static class MyRunAction extends DumbAwareAction{
212     private final ScriptEngine myEngine;
213
214     public MyRunAction(ScriptEngine engine) {
215       myEngine = engine;
216     }
217
218     @Override
219     public void update(AnActionEvent e) {
220       Project project = e.getProject();
221       Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
222       VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext());
223       e.getPresentation().setEnabledAndVisible(project != null && editor != null && virtualFile != null);
224     }
225
226     @Override
227     public void actionPerformed(AnActionEvent e) {
228       Project project = e.getProject();
229       Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
230       VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext());
231       if (project == null || editor == null || virtualFile == null) return;
232       executeQuery(project, virtualFile, editor, myEngine);
233     }
234   }
235 }