[project] IDE launcher in Windows jump list instead of the custom one (IDEA-156078)
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / StudyUtils.java
1 package com.jetbrains.edu.learning;
2
3 import com.intellij.execution.RunContentExecutor;
4 import com.intellij.execution.configurations.GeneralCommandLine;
5 import com.intellij.execution.process.ProcessHandler;
6 import com.intellij.lang.Language;
7 import com.intellij.openapi.actionSystem.AnActionEvent;
8 import com.intellij.openapi.actionSystem.CommonDataKeys;
9 import com.intellij.openapi.actionSystem.DataContext;
10 import com.intellij.openapi.actionSystem.Presentation;
11 import com.intellij.openapi.application.ApplicationManager;
12 import com.intellij.openapi.diagnostic.Logger;
13 import com.intellij.openapi.editor.Document;
14 import com.intellij.openapi.editor.Editor;
15 import com.intellij.openapi.editor.RangeMarker;
16 import com.intellij.openapi.editor.actionSystem.EditorActionManager;
17 import com.intellij.openapi.editor.colors.EditorColors;
18 import com.intellij.openapi.editor.impl.DocumentImpl;
19 import com.intellij.openapi.fileEditor.FileDocumentManager;
20 import com.intellij.openapi.fileEditor.FileEditor;
21 import com.intellij.openapi.fileEditor.FileEditorManager;
22 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.projectRoots.Sdk;
25 import com.intellij.openapi.ui.popup.Balloon;
26 import com.intellij.openapi.util.Disposer;
27 import com.intellij.openapi.util.io.FileUtil;
28 import com.intellij.openapi.vfs.VfsUtil;
29 import com.intellij.openapi.vfs.VirtualFile;
30 import com.intellij.openapi.wm.ToolWindow;
31 import com.intellij.openapi.wm.ToolWindowManager;
32 import com.intellij.psi.PsiDirectory;
33 import com.intellij.psi.PsiElement;
34 import com.intellij.psi.PsiFile;
35 import com.intellij.ui.JBColor;
36 import com.intellij.ui.awt.RelativePoint;
37 import com.intellij.ui.content.Content;
38 import com.intellij.util.ui.UIUtil;
39 import com.jetbrains.edu.learning.checker.StudyExecutor;
40 import com.jetbrains.edu.learning.checker.StudyTestRunner;
41 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderDeleteHandler;
42 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter;
43 import com.jetbrains.edu.learning.core.EduNames;
44 import com.jetbrains.edu.learning.core.EduUtils;
45 import com.jetbrains.edu.learning.courseFormat.*;
46 import com.jetbrains.edu.learning.editor.StudyEditor;
47 import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory;
48 import com.jetbrains.edu.learning.ui.StudyToolWindow;
49 import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
50 import org.jetbrains.annotations.NotNull;
51 import org.jetbrains.annotations.Nullable;
52
53 import javax.swing.*;
54 import java.awt.*;
55 import java.io.*;
56 import java.util.Collection;
57 import java.util.List;
58
59 public class StudyUtils {
60   private StudyUtils() {
61   }
62
63   private static final Logger LOG = Logger.getInstance(StudyUtils.class.getName());
64   private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description";
65
66   public static void closeSilently(@Nullable final Closeable stream) {
67     if (stream != null) {
68       try {
69         stream.close();
70       }
71       catch (IOException e) {
72         // close silently
73       }
74     }
75   }
76
77   public static boolean isZip(String fileName) {
78     return fileName.contains(".zip");
79   }
80
81   public static <T> T getFirst(@NotNull final Iterable<T> container) {
82     return container.iterator().next();
83   }
84
85   public static boolean indexIsValid(int index, @NotNull final Collection collection) {
86     int size = collection.size();
87     return index >= 0 && index < size;
88   }
89
90   @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
91   @Nullable
92   public static String getFileText(@Nullable final String parentDir, @NotNull final String fileName, boolean wrapHTML,
93                                    @NotNull final String encoding) {
94     final File inputFile = parentDir != null ? new File(parentDir, fileName) : new File(fileName);
95     if (!inputFile.exists()) return null;
96     final StringBuilder taskText = new StringBuilder();
97     BufferedReader reader = null;
98     try {
99       reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), encoding));
100       String line;
101       while ((line = reader.readLine()) != null) {
102         taskText.append(line).append("\n");
103         if (wrapHTML) {
104           taskText.append("<br>");
105         }
106       }
107       return wrapHTML ? UIUtil.toHtml(taskText.toString()) : taskText.toString();
108     }
109     catch (IOException e) {
110       LOG.info("Failed to get file text from file " + fileName, e);
111     }
112     finally {
113       closeSilently(reader);
114     }
115     return null;
116   }
117
118   public static void updateAction(@NotNull final AnActionEvent e) {
119     final Presentation presentation = e.getPresentation();
120     presentation.setEnabled(false);
121     final Project project = e.getProject();
122     if (project != null) {
123       final StudyEditor studyEditor = getSelectedStudyEditor(project);
124       if (studyEditor != null) {
125         presentation.setEnabledAndVisible(true);
126       }
127     }
128   }
129
130   public static void updateToolWindows(@NotNull final Project project) {
131     updateStudyToolWindow(project);
132
133     final ToolWindowManager windowManager = ToolWindowManager.getInstance(project);
134     createProgressToolWindowContent(project, windowManager);
135   }
136
137   public static void initToolWindows(@NotNull final Project project) {
138     final ToolWindowManager windowManager = ToolWindowManager.getInstance(project);
139     windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW).getContentManager().removeAllContents(false);
140     StudyToolWindowFactory factory = new StudyToolWindowFactory();
141     factory.createToolWindowContent(project, windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW));
142
143     createProgressToolWindowContent(project, windowManager);
144   }
145
146   private static void createProgressToolWindowContent(@NotNull Project project, ToolWindowManager windowManager) {
147     windowManager.getToolWindow(StudyProgressToolWindowFactory.ID).getContentManager().removeAllContents(false);
148     StudyProgressToolWindowFactory windowFactory = new StudyProgressToolWindowFactory();
149     windowFactory.createToolWindowContent(project, windowManager.getToolWindow(StudyProgressToolWindowFactory.ID));
150   }
151   
152   @Nullable
153   public static StudyToolWindow getStudyToolWindow(@NotNull final Project project) {
154     ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
155     if (toolWindow != null) {
156       Content[] contents = toolWindow.getContentManager().getContents();
157       for (Content content: contents) {
158         JComponent component = content.getComponent();
159         if (component != null && component instanceof StudyToolWindow) {
160           return (StudyToolWindow)component;
161         }
162       }
163     }
164     return null;
165   }
166
167   public static void deleteFile(@NotNull final VirtualFile file) {
168     try {
169       file.delete(StudyUtils.class);
170     }
171     catch (IOException e) {
172       LOG.error(e);
173     }
174   }
175
176   public static File copyResourceFile(@NotNull final String sourceName, @NotNull final String copyName, @NotNull final Project project,
177                                       @NotNull final Task task)
178     throws IOException {
179     final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
180     final Course course = taskManager.getCourse();
181     int taskNum = task.getIndex();
182     int lessonNum = task.getLesson().getIndex();
183     assert course != null;
184     final String pathToResource = FileUtil.join(course.getCourseDirectory(), EduNames.LESSON + lessonNum, EduNames.TASK + taskNum);
185     final File resourceFile = new File(pathToResource, copyName);
186     FileUtil.copy(new File(pathToResource, sourceName), resourceFile);
187     return resourceFile;
188   }
189
190   @Nullable
191   public static Sdk findSdk(@NotNull final Task task, @NotNull final Project project) {
192     final Language language = task.getLesson().getCourse().getLanguageById();
193     return StudyExecutor.INSTANCE.forLanguage(language).findSdk(project);
194   }
195
196   @NotNull
197   public static StudyTestRunner getTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
198     final Language language = task.getLesson().getCourse().getLanguageById();
199     return StudyExecutor.INSTANCE.forLanguage(language).getTestRunner(task, taskDir);
200   }
201
202   public static RunContentExecutor getExecutor(@NotNull final Project project, @NotNull final Task currentTask,
203                                                @NotNull final ProcessHandler handler) {
204     final Language language = currentTask.getLesson().getCourse().getLanguageById();
205     return StudyExecutor.INSTANCE.forLanguage(language).getExecutor(project, handler);
206   }
207
208   public static void setCommandLineParameters(@NotNull final GeneralCommandLine cmd,
209                                               @NotNull final Project project,
210                                               @NotNull final String filePath,
211                                               @NotNull final String sdkPath,
212                                               @NotNull final Task currentTask) {
213     final Language language = currentTask.getLesson().getCourse().getLanguageById();
214     StudyExecutor.INSTANCE.forLanguage(language).setCommandLineParameters(cmd, project, filePath, sdkPath, currentTask);
215   }
216
217   public static void showNoSdkNotification(@NotNull final Task currentTask, @NotNull final Project project) {
218     final Language language = currentTask.getLesson().getCourse().getLanguageById();
219     StudyExecutor.INSTANCE.forLanguage(language).showNoSdkNotification(project);
220   }
221
222
223   /**
224    * shows pop up in the center of "check task" button in study editor
225    */
226   public static void showCheckPopUp(@NotNull final Project project, @NotNull final Balloon balloon) {
227     final StudyEditor studyEditor = getSelectedStudyEditor(project);
228     assert studyEditor != null;
229
230     balloon.show(computeLocation(studyEditor.getEditor()), Balloon.Position.above);
231     Disposer.register(project, balloon);
232   }
233
234   public static RelativePoint computeLocation(Editor editor){
235
236     final Rectangle visibleRect = editor.getComponent().getVisibleRect();
237     Point point = new Point(visibleRect.x + visibleRect.width + 10,
238                             visibleRect.y + 10);
239     return new RelativePoint(editor.getComponent(), point);
240   }
241
242
243   /**
244    * returns language manager which contains all the information about language specific file names
245    */
246   @Nullable
247   public static StudyLanguageManager getLanguageManager(@NotNull final Course course) {
248     Language language = course.getLanguageById();
249     return language == null ? null : StudyLanguageManager.INSTANCE.forLanguage(language);
250   }
251
252   public static boolean isTestsFile(@NotNull Project project, @NotNull final String name) {
253     Course course = StudyTaskManager.getInstance(project).getCourse();
254     if (course == null) {
255       return false;
256     }
257     StudyLanguageManager manager = getLanguageManager(course);
258     if (manager == null) {
259       return false;
260     }
261     return manager.getTestFileName().equals(name);
262   }
263
264   @Nullable
265   public static TaskFile getTaskFile(@NotNull final Project project, @NotNull final VirtualFile file) {
266     final Course course = StudyTaskManager.getInstance(project).getCourse();
267     if (course == null) {
268       return null;
269     }
270     VirtualFile taskDir = file.getParent();
271     if (taskDir == null) {
272       return null;
273     }
274     //need this because of multi-module generation
275     if ("src".equals(taskDir.getName())) {
276       taskDir = taskDir.getParent();
277       if (taskDir == null) {
278         return null;
279       }
280     }
281     final String taskDirName = taskDir.getName();
282     if (taskDirName.contains(EduNames.TASK)) {
283       final VirtualFile lessonDir = taskDir.getParent();
284       if (lessonDir != null) {
285         int lessonIndex = EduUtils.getIndex(lessonDir.getName(), EduNames.LESSON);
286         List<Lesson> lessons = course.getLessons();
287         if (!indexIsValid(lessonIndex, lessons)) {
288           return null;
289         }
290         final Lesson lesson = lessons.get(lessonIndex);
291         int taskIndex = EduUtils.getIndex(taskDirName, EduNames.TASK);
292         final List<Task> tasks = lesson.getTaskList();
293         if (!indexIsValid(taskIndex, tasks)) {
294           return null;
295         }
296         final Task task = tasks.get(taskIndex);
297         return task.getFile(file.getName());
298       }
299     }
300     return null;
301   }
302
303   public static void drawAllWindows(Editor editor, TaskFile taskFile) {
304     editor.getMarkupModel().removeAllHighlighters();
305     final Project project = editor.getProject();
306     if (project == null) return;
307     final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
308     for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) {
309       final JBColor color = taskManager.getColor(answerPlaceholder);
310       EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, color);
311     }
312     final Document document = editor.getDocument();
313     EditorActionManager.getInstance()
314       .setReadonlyFragmentModificationHandler(document, new EduAnswerPlaceholderDeleteHandler(editor));
315     EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile);
316     editor.getColorsScheme().setColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR, null);
317   }
318
319   @Nullable
320   public static StudyEditor getSelectedStudyEditor(@NotNull final Project project) {
321     try {
322       final FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow().
323         getSelectedEditor().getSelectedEditorWithProvider().getFirst();
324       if (fileEditor instanceof StudyEditor) {
325         return (StudyEditor)fileEditor;
326       }
327     }
328     catch (Exception e) {
329       return null;
330     }
331     return null;
332   }
333
334   @Nullable
335   public static Editor getSelectedEditor(@NotNull final Project project) {
336     final StudyEditor studyEditor = getSelectedStudyEditor(project);
337     if (studyEditor != null) {
338       return studyEditor.getEditor();
339     }
340     return null;
341   }
342
343   public static void deleteGuardedBlocks(@NotNull final Document document) {
344     if (document instanceof DocumentImpl) {
345       final DocumentImpl documentImpl = (DocumentImpl)document;
346       List<RangeMarker> blocks = documentImpl.getGuardedBlocks();
347       for (final RangeMarker block : blocks) {
348         ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> {
349           document.removeGuardedBlock(block);
350         }));
351       }
352     }
353   }
354
355
356   @Nullable
357   public static VirtualFile getPatternFile(@NotNull TaskFile taskFile, String name) {
358     Task task = taskFile.getTask();
359     String lessonDir = EduNames.LESSON + String.valueOf(task.getLesson().getIndex());
360     String taskDir = EduNames.TASK + String.valueOf(task.getIndex());
361     Course course = task.getLesson().getCourse();
362     File resourceFile = new File(course.getCourseDirectory());
363     if (!resourceFile.exists()) {
364       return null;
365     }
366     String patternPath = FileUtil.join(resourceFile.getPath(), lessonDir, taskDir, name);
367     VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true);
368     if (patternFile == null) {
369       return null;
370     }
371     return patternFile;
372   }
373
374   @Nullable
375   public static Document getPatternDocument(@NotNull final TaskFile taskFile, String name) {
376     VirtualFile patternFile = getPatternFile(taskFile, name);
377     if (patternFile == null) {
378       return null;
379     }
380     return FileDocumentManager.getInstance().getDocument(patternFile);
381   }
382
383   public static boolean isRenameableOrMoveable(@NotNull final Project project, @NotNull final Course course, @NotNull final PsiElement element) {
384     if (element instanceof PsiFile) {
385       VirtualFile virtualFile = ((PsiFile)element).getVirtualFile();
386       if (project.getBaseDir().equals(virtualFile.getParent())) {
387         return false;
388       }
389       TaskFile file = getTaskFile(project, virtualFile);
390       if (file != null) {
391         return false;
392       }
393       String name = virtualFile.getName();
394       return !isTestsFile(project, name) && !EduNames.TASK_HTML.equals(name);
395     }
396     if (element instanceof PsiDirectory) {
397       VirtualFile virtualFile = ((PsiDirectory)element).getVirtualFile();
398       VirtualFile parent = virtualFile.getParent();
399       if (parent == null) {
400         return true;
401       }
402       if (project.getBaseDir().equals(parent)) {
403         return false;
404       }
405       Lesson lesson = course.getLesson(parent.getName());
406       if (lesson != null) {
407         Task task = lesson.getTask(virtualFile.getName());
408         if (task != null) {
409           return false;
410         }
411       }
412     }
413     return true;
414   }
415
416   public static boolean canRenameOrMove(DataContext dataContext) {
417     Project project = CommonDataKeys.PROJECT.getData(dataContext);
418     PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
419     if (element == null || project == null) {
420       return false;
421     }
422     Course course = StudyTaskManager.getInstance(project).getCourse();
423     if (course == null || !EduNames.STUDY.equals(course.getCourseMode())) {
424       return false;
425     }
426
427     if (!isRenameableOrMoveable(project, course, element)) {
428       return true;
429     }
430     return false;
431   }
432
433   @Nullable
434   public static String getTaskTextFromTask(@Nullable final Task task, @Nullable final VirtualFile taskDirectory) {
435     if (task == null) {
436       return null;
437     }
438     String text = task.getText();
439     if (text != null) {
440       return text;
441     }
442     if (taskDirectory != null) {
443       VirtualFile taskTextFile = taskDirectory.findChild(EduNames.TASK_HTML);
444       if (taskTextFile == null) {
445         VirtualFile srcDir = taskDirectory.findChild("src");
446         if (srcDir != null) {
447            taskTextFile = srcDir.findChild(EduNames.TASK_HTML);
448         }
449       }
450       if (taskTextFile != null) {
451         try {
452           return FileUtil.loadTextAndClose(taskTextFile.getInputStream());
453         }
454         catch (IOException e) {
455           LOG.info(e);
456         }
457       }
458     }
459     return null;
460   }
461   
462   @Nullable
463   public static StudyPluginConfigurator getConfigurator(@NotNull final Project project) {
464     StudyPluginConfigurator[] extensions = StudyPluginConfigurator.EP_NAME.getExtensions();
465     for (StudyPluginConfigurator extension: extensions) {
466       if (extension.accept(project)) {
467         return extension;
468       }
469     }
470     return null;
471   }
472
473   @Nullable
474   public static StudyTwitterPluginConfigurator getTwitterConfigurator(@NotNull final Project project) {
475     StudyTwitterPluginConfigurator[] extensions = StudyTwitterPluginConfigurator.EP_NAME.getExtensions();
476     for (StudyTwitterPluginConfigurator extension: extensions) {
477       if (extension.accept(project)) {
478         return extension;
479       }
480     }
481     return null;
482   }
483
484
485   public static String getTaskText(@NotNull final Project project) {
486     TaskFile taskFile = getSelectedTaskFile(project);
487     if (taskFile == null) {
488       return EMPTY_TASK_TEXT;
489     }
490     final Task task = taskFile.getTask();
491     if (task != null) {
492       return getTaskTextFromTask(task, task.getTaskDir(project));
493     }
494     return null;
495   }
496   @Nullable
497   public static TaskFile getSelectedTaskFile(@NotNull Project project) {
498     VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles();
499     TaskFile taskFile = null;
500     for (VirtualFile file : files) {
501       taskFile = getTaskFile(project, file);
502       if (taskFile != null) {
503         break;
504       }
505     }
506     return taskFile;
507   }
508
509   public static void updateStudyToolWindow(Project project) {
510     final StudyToolWindow studyToolWindow = getStudyToolWindow(project);
511     if (studyToolWindow != null) {
512       String taskText = getTaskText(project);
513       studyToolWindow.setTaskText(taskText, null, project);
514     }
515   }
516
517   public static boolean isStudyProject(@NotNull Project project) {
518     return StudyTaskManager.getInstance(project).getCourse() != null;
519   }
520
521   public static boolean hasJavaFx() {
522     try {
523       Class.forName("javafx.application.Platform");
524       return true;
525     }
526     catch (ClassNotFoundException e) {
527       return false;
528     }
529   }
530
531   @Nullable
532   public static Task getTask(@NotNull Project project, @NotNull VirtualFile taskVF) {
533     Course course = StudyTaskManager.getInstance(project).getCourse();
534     if (course == null) {
535       return null;
536     }
537     VirtualFile lessonVF = taskVF.getParent();
538     if (lessonVF == null) {
539       return null;
540     }
541     Lesson lesson = course.getLesson(lessonVF.getName());
542     if (lesson == null) {
543       return null;
544     }
545     return lesson.getTask(taskVF.getName());
546   }
547
548   @Nullable
549   public static VirtualFile getTaskDir(@NotNull VirtualFile taskFile) {
550     VirtualFile parent = taskFile.getParent();
551     if (parent == null) {
552       return null;
553     }
554     String name = parent.getName();
555     if (name.contains(EduNames.TASK)) {
556       return parent;
557     }
558     if (EduNames.SRC.equals(name)) {
559       return parent.getParent();
560     }
561     return null;
562   }
563 }