IDEA-136705 (delay post-execution refresh to allow FS changes to reach the IDE)
[idea/community.git] / python / educational / interactive-learning / src / com / jetbrains / edu / learning / actions / StudyEditInputAction.java
1 package com.jetbrains.edu.learning.actions;
2
3 import com.intellij.icons.AllIcons;
4 import com.intellij.ide.ui.UISettings;
5 import com.intellij.openapi.actionSystem.*;
6 import com.intellij.openapi.application.ApplicationManager;
7 import com.intellij.openapi.diagnostic.Logger;
8 import com.intellij.openapi.editor.Editor;
9 import com.intellij.openapi.fileEditor.FileDocumentManager;
10 import com.intellij.openapi.project.DumbAware;
11 import com.intellij.openapi.project.DumbAwareAction;
12 import com.intellij.openapi.project.Project;
13 import com.intellij.openapi.ui.popup.JBPopup;
14 import com.intellij.openapi.ui.popup.JBPopupAdapter;
15 import com.intellij.openapi.ui.popup.JBPopupFactory;
16 import com.intellij.openapi.ui.popup.LightweightWindowEvent;
17 import com.intellij.openapi.vfs.VirtualFile;
18 import com.intellij.openapi.wm.IdeFocusManager;
19 import com.intellij.ui.tabs.TabInfo;
20 import com.intellij.ui.tabs.TabsListener;
21 import com.intellij.ui.tabs.impl.JBEditorTabs;
22 import com.intellij.util.PlatformIcons;
23 import com.jetbrains.edu.EduNames;
24 import com.jetbrains.edu.EduUtils;
25 import com.jetbrains.edu.courseFormat.Task;
26 import com.jetbrains.edu.courseFormat.TaskFile;
27 import com.jetbrains.edu.learning.StudyTaskManager;
28 import com.jetbrains.edu.learning.StudyUtils;
29 import com.jetbrains.edu.learning.courseFormat.UserTest;
30 import com.jetbrains.edu.learning.editor.StudyEditor;
31 import com.jetbrains.edu.learning.ui.StudyTestContentPanel;
32 import org.jetbrains.annotations.NotNull;
33
34 import javax.swing.*;
35 import java.io.File;
36 import java.io.FileNotFoundException;
37 import java.io.FileOutputStream;
38 import java.io.PrintWriter;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42
43 public class StudyEditInputAction extends DumbAwareAction {
44
45   private static final Logger LOG = Logger.getInstance(StudyEditInputAction.class.getName());
46   private JBEditorTabs tabbedPane;
47   private Map<TabInfo, UserTest> myEditableTabs = new HashMap<TabInfo, UserTest>();
48
49   public void showInput(final Project project) {
50     final Editor selectedEditor = StudyEditor.getSelectedEditor(project);
51     if (selectedEditor != null) {
52       FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
53       final VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
54       final StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(project);
55       assert openedFile != null;
56       TaskFile taskFile = StudyUtils.getTaskFile(project, openedFile);
57       assert taskFile != null;
58       final Task currentTask = taskFile.getTask();
59       tabbedPane = new JBEditorTabs(project, ActionManager.getInstance(), IdeFocusManager.findInstance(), project);
60       tabbedPane.addListener(new TabsListener.Adapter() {
61         @Override
62         public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) {
63           if (newSelection.getIcon() != null) {
64             int tabCount = tabbedPane.getTabCount();
65             VirtualFile taskDir = openedFile.getParent();
66             VirtualFile testsDir = taskDir.findChild(EduNames.USER_TESTS);
67             assert testsDir != null;
68             UserTest userTest = createUserTest(testsDir, currentTask, studyTaskManager);
69             userTest.setEditable(true);
70             StudyTestContentPanel testContentPanel = new StudyTestContentPanel(userTest);
71             TabInfo testTab = addTestTab(tabbedPane.getTabCount(), testContentPanel, currentTask, true);
72             myEditableTabs.put(testTab, userTest);
73             tabbedPane.addTabSilently(testTab, tabCount - 1);
74             tabbedPane.select(testTab, true);
75           }
76         }
77       });
78
79       List<UserTest> userTests = studyTaskManager.getUserTests(currentTask);
80       int i = 1;
81       for (UserTest userTest : userTests) {
82         String inputFileText = StudyUtils.getFileText(null, userTest.getInput(), false, "UTF-8");
83         String outputFileText = StudyUtils.getFileText(null, userTest.getOutput(), false, "UTF-8");
84         StudyTestContentPanel myContentPanel = new StudyTestContentPanel(userTest);
85         myContentPanel.addInputContent(inputFileText);
86         myContentPanel.addOutputContent(outputFileText);
87         TabInfo testTab = addTestTab(i, myContentPanel, currentTask, userTest.isEditable());
88         tabbedPane.addTabSilently(testTab, i - 1);
89         if (userTest.isEditable()) {
90           myEditableTabs.put(testTab, userTest);
91         }
92         i++;
93       }
94       TabInfo plusTab = new TabInfo(new JPanel());
95       plusTab.setIcon(PlatformIcons.ADD_ICON);
96       tabbedPane.addTabSilently(plusTab, tabbedPane.getTabCount());
97       final JBPopup hint =
98         JBPopupFactory.getInstance().createComponentPopupBuilder(tabbedPane.getComponent(), tabbedPane.getComponent())
99           .setResizable(true)
100           .setMovable(true)
101           .setRequestFocus(true)
102           .createPopup();
103       StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
104       assert selectedStudyEditor != null;
105       hint.showInCenterOf(selectedStudyEditor.getComponent());
106       hint.addListener(new HintClosedListener(currentTask, studyTaskManager));
107     }
108   }
109
110
111   private static void flushBuffer(@NotNull final StringBuilder buffer, @NotNull final File file) {
112     PrintWriter printWriter = null;
113     try {
114       //noinspection IOResourceOpenedButNotSafelyClosed
115       printWriter = new PrintWriter(new FileOutputStream(file));
116       printWriter.print(buffer.toString());
117     }
118     catch (FileNotFoundException e) {
119       LOG.error(e);
120     }
121     finally {
122       StudyUtils.closeSilently(printWriter);
123     }
124     EduUtils.synchronize();
125   }
126
127   private static UserTest createUserTest(@NotNull final VirtualFile testsDir,
128                                          @NotNull final Task currentTask,
129                                          StudyTaskManager studyTaskManager) {
130     UserTest userTest = new UserTest();
131     List<UserTest> userTests = studyTaskManager.getUserTests(currentTask);
132     int testNum = userTests.size() + 1;
133     String inputName = EduNames.USER_TEST_INPUT + testNum;
134     File inputFile = new File(testsDir.getPath(), inputName);
135     String outputName = EduNames.USER_TEST_OUTPUT + testNum;
136     File outputFile = new File(testsDir.getPath(), outputName);
137     userTest.setInput(inputFile.getPath());
138     userTest.setOutput(outputFile.getPath());
139     studyTaskManager.addUserTest(currentTask, userTest);
140     return userTest;
141   }
142
143   private TabInfo addTestTab(int nameIndex, final StudyTestContentPanel contentPanel, @NotNull final Task currentTask, boolean toBeClosable) {
144     TabInfo testTab = toBeClosable ? createClosableTab(contentPanel, currentTask) : new TabInfo(contentPanel);
145     return testTab.setText(EduNames.TEST_TAB_NAME + String.valueOf(nameIndex));
146   }
147
148   private TabInfo createClosableTab(StudyTestContentPanel contentPanel, Task currentTask) {
149     TabInfo closableTab = new TabInfo(contentPanel);
150     final DefaultActionGroup tabActions = new DefaultActionGroup();
151     tabActions.add(new CloseTab(closableTab, currentTask));
152     closableTab.setTabLabelActions(tabActions, ActionPlaces.EDITOR_TAB);
153     return closableTab;
154   }
155
156   public void actionPerformed(AnActionEvent e) {
157     showInput(e.getProject());
158   }
159
160   private static class HintClosedListener extends JBPopupAdapter {
161     private final Task myTask;
162     private final StudyTaskManager myStudyTaskManager;
163
164     private HintClosedListener(@NotNull final Task task, StudyTaskManager studyTaskManager) {
165       myTask = task;
166       myStudyTaskManager = studyTaskManager;
167     }
168
169     @Override
170     public void onClosed(LightweightWindowEvent event) {
171       for (final UserTest userTest : myStudyTaskManager.getUserTests(myTask)) {
172         ApplicationManager.getApplication().runWriteAction(new Runnable() {
173           @Override
174           public void run() {
175             if (userTest.isEditable()) {
176               File inputFile = new File(userTest.getInput());
177               File outputFile = new File(userTest.getOutput());
178               flushBuffer(userTest.getInputBuffer(), inputFile);
179               flushBuffer(userTest.getOutputBuffer(), outputFile);
180             }
181           }
182         });
183       }
184     }
185   }
186
187   private class CloseTab extends AnAction implements DumbAware {
188
189     private final TabInfo myTabInfo;
190     private final Task myTask;
191
192     public CloseTab(final TabInfo info, @NotNull final Task task) {
193       myTabInfo = info;
194       myTask = task;
195     }
196
197     @Override
198     public void update(final AnActionEvent e) {
199       e.getPresentation().setIcon(AllIcons.Actions.Close);
200       e.getPresentation().setHoveredIcon(AllIcons.Actions.CloseHovered);
201       e.getPresentation().setVisible(UISettings.getInstance().SHOW_CLOSE_BUTTON);
202       e.getPresentation().setText("Delete test");
203     }
204
205     @Override
206     public void actionPerformed(final AnActionEvent e) {
207       tabbedPane.removeTab(myTabInfo);
208       UserTest userTest = myEditableTabs.get(myTabInfo);
209       File testInputFile = new File(userTest.getInput());
210       File testOutputFile = new File(userTest.getOutput());
211       if (testInputFile.delete() && testOutputFile.delete()) {
212         EduUtils.synchronize();
213       } else {
214         LOG.error("failed to delete user tests");
215       }
216       final Project project = e.getProject();
217       if (project != null) {
218         StudyTaskManager.getInstance(project).removeUserTest(myTask, userTest);
219       }
220     }
221   }
222 }