Fix #EDU-565 The shortcut for "check task" doesn't work
[idea/community.git] / python / educational-python / student-python / src / com / jetbrains / edu / learning / PyStudyCheckAction.java
1 package com.jetbrains.edu.learning;
2
3 import com.intellij.execution.ExecutionException;
4 import com.intellij.openapi.actionSystem.ActionManager;
5 import com.intellij.openapi.actionSystem.AnActionEvent;
6 import com.intellij.openapi.actionSystem.KeyboardShortcut;
7 import com.intellij.openapi.actionSystem.Presentation;
8 import com.intellij.openapi.application.ApplicationManager;
9 import com.intellij.openapi.command.CommandProcessor;
10 import com.intellij.openapi.diagnostic.Logger;
11 import com.intellij.openapi.keymap.KeymapUtil;
12 import com.intellij.openapi.progress.ProgressManager;
13 import com.intellij.openapi.project.DumbService;
14 import com.intellij.openapi.project.Project;
15 import com.intellij.openapi.ui.MessageType;
16 import com.intellij.openapi.util.Ref;
17 import com.intellij.openapi.vfs.VirtualFile;
18 import com.intellij.openapi.wm.IdeFocusManager;
19 import com.jetbrains.edu.courseFormat.StudyStatus;
20 import com.jetbrains.edu.courseFormat.Task;
21 import com.jetbrains.edu.courseFormat.TaskFile;
22 import com.jetbrains.edu.learning.actions.StudyRunAction;
23 import com.jetbrains.edu.learning.actions.StudyToolbarAction;
24 import com.jetbrains.edu.learning.checker.StudyCheckTask;
25 import com.jetbrains.edu.learning.checker.StudyCheckUtils;
26 import com.jetbrains.edu.learning.checker.StudyTestRunner;
27 import com.jetbrains.edu.learning.checker.StudyTestsOutputParser;
28 import com.jetbrains.edu.learning.editor.StudyEditor;
29 import icons.InteractiveLearningIcons;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 import javax.swing.*;
34 import java.util.Map;
35
36 public class PyStudyCheckAction extends StudyToolbarAction {
37   private static final Logger LOG = Logger.getInstance(PyStudyCheckAction.class);
38
39   public static final String ACTION_ID = "PyCheckAction";
40   public static final String SHORTCUT = "ctrl alt pressed ENTER";
41
42   protected Ref<Boolean> myCheckInProgress = new Ref<>(false);
43
44   public PyStudyCheckAction() {
45     super("Check Task (" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")", "Check current task", InteractiveLearningIcons.Resolve);
46   }
47
48   @Override
49   public void actionPerformed(@NotNull AnActionEvent e) {
50     Project project = e.getProject();
51     if (project == null) {
52       return;
53     }
54     if (DumbService.isDumb(project)) {
55       StudyCheckUtils.showTestResultPopUp("Checking is not available while indexing is in progress", MessageType.WARNING.getPopupBackground(), project);
56       return;
57     }
58     check(project);
59   }
60   
61   protected void check(@NotNull Project project) {
62     ApplicationManager.getApplication().runWriteAction(() -> {
63       CommandProcessor.getInstance().runUndoTransparentAction(() -> {
64         final StudyEditor selectedEditor = StudyUtils.getSelectedStudyEditor(project);
65         if (selectedEditor == null) return;
66         final StudyState studyState = new StudyState(selectedEditor);
67         if (!studyState.isValid()) {
68           LOG.info("StudyCheckAction was invoked outside study editor");
69           return;
70         }
71         if (StudyCheckUtils.hasBackgroundProcesses(project)) return;
72
73
74         if (!runTask(project)) return;
75
76         final Task task = studyState.getTask();
77         final VirtualFile taskDir = studyState.getTaskDir();
78         StudyCheckUtils.flushWindows(task, taskDir);
79
80
81         ApplicationManager.getApplication().invokeLater(
82           () -> IdeFocusManager.getInstance(project).requestFocus(studyState.getEditor().getComponent(), true));
83
84         final StudyTestRunner testRunner = StudyUtils.getTestRunner(task, taskDir);
85         Process testProcess = null;
86         String commandLine = "";
87         try {
88           final VirtualFile executablePath = getTaskVirtualFile(studyState, task, taskDir);
89           if (executablePath != null) {
90             commandLine = executablePath.getPath();
91             testProcess = testRunner.createCheckProcess(project, commandLine);
92           }
93         }
94         catch (ExecutionException e) {
95           LOG.error(e);
96         }
97         if (testProcess == null) {
98           return;
99         }
100         myCheckInProgress.set(true);
101         StudyCheckTask checkTask = getCheckTask(project, studyState, testRunner, testProcess, commandLine);
102         ProgressManager.getInstance().run(checkTask);
103       });
104     });
105   }
106
107   private static boolean runTask(@NotNull Project project) {
108     final StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
109     if (runAction == null) {
110       return false;
111     }
112     runAction.run(project);
113     return true;
114   }
115
116   @NotNull
117   private StudyCheckTask getCheckTask(@NotNull final Project project,
118                                       final StudyState studyState,
119                                       final StudyTestRunner testRunner,
120                                       final Process testProcess,
121                                       final String commandLine) {
122     return new StudyCheckTask(project, studyState, myCheckInProgress, testProcess, commandLine) {
123             @Override
124             protected void onTaskFailed(StudyTestsOutputParser.TestsOutput testsOutput) {
125               ApplicationManager.getApplication().invokeLater(() -> {
126                 if (myTaskDir == null) return;
127                 myTaskManger.setStatus(myTask, StudyStatus.Failed);
128                 for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
129                   final String name = entry.getKey();
130                   final TaskFile taskFile = entry.getValue();
131                   if (taskFile.getAnswerPlaceholders().size() < 2) {
132                     myTaskManger.setStatus(taskFile, StudyStatus.Failed);
133                     continue;
134                   }
135                   CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
136                     StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
137                   }));
138                 }
139                 StudyCheckUtils.showTestResultPopUp(testsOutput.getMessage(), MessageType.ERROR.getPopupBackground(), project);
140                 StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
141               });
142             }
143           };
144   }
145
146
147   @Nullable
148   private static VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState,
149                                          @NotNull final Task task,
150                                          @NotNull final VirtualFile taskDir) {
151     VirtualFile taskVirtualFile = studyState.getVirtualFile();
152     for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
153       String name = entry.getKey();
154       TaskFile taskFile = entry.getValue();
155       VirtualFile virtualFile = taskDir.findChild(name);
156       if (virtualFile != null) {
157         if (!taskFile.getAnswerPlaceholders().isEmpty()) {
158           taskVirtualFile = virtualFile;
159         }
160       }
161     }
162     return taskVirtualFile;
163   }
164
165
166
167   @Override
168   public String getActionId() {
169     return ACTION_ID;
170   }
171
172   @Override
173   public String[] getShortcuts() {
174     return new String[]{SHORTCUT};
175   }
176
177   @Override
178   public void update(AnActionEvent e) {
179     final Presentation presentation = e.getPresentation();
180     StudyUtils.updateAction(e);
181     if (presentation.isEnabled()) {
182       presentation.setEnabled(!myCheckInProgress.get());
183     }
184   }
185 }