1 package com.jetbrains.edu.learning;
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.colors.EditorColorsManager;
19 import com.intellij.openapi.editor.impl.DocumentImpl;
20 import com.intellij.openapi.fileEditor.FileDocumentManager;
21 import com.intellij.openapi.fileEditor.FileEditor;
22 import com.intellij.openapi.fileEditor.FileEditorManager;
23 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
24 import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
25 import com.intellij.openapi.progress.ProgressManager;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.project.ProjectManager;
28 import com.intellij.openapi.projectRoots.Sdk;
29 import com.intellij.openapi.ui.MessageType;
30 import com.intellij.openapi.ui.popup.Balloon;
31 import com.intellij.openapi.ui.popup.JBPopupFactory;
32 import com.intellij.openapi.util.Disposer;
33 import com.intellij.openapi.util.io.FileUtil;
34 import com.intellij.openapi.util.io.FileUtilRt;
35 import com.intellij.openapi.util.text.StringUtil;
36 import com.intellij.openapi.vfs.LocalFileSystem;
37 import com.intellij.openapi.vfs.VfsUtil;
38 import com.intellij.openapi.vfs.VirtualFile;
39 import com.intellij.openapi.wm.IdeFocusManager;
40 import com.intellij.openapi.wm.ToolWindow;
41 import com.intellij.openapi.wm.ToolWindowAnchor;
42 import com.intellij.openapi.wm.ToolWindowManager;
43 import com.intellij.psi.PsiDirectory;
44 import com.intellij.psi.PsiElement;
45 import com.intellij.psi.PsiFile;
46 import com.intellij.ui.JBColor;
47 import com.intellij.ui.awt.RelativePoint;
48 import com.intellij.ui.content.Content;
49 import com.intellij.util.ObjectUtils;
50 import com.intellij.util.TimeoutUtil;
51 import com.intellij.util.containers.ContainerUtil;
52 import com.intellij.util.text.MarkdownUtil;
53 import com.intellij.util.ui.UIUtil;
54 import com.jetbrains.edu.learning.checker.StudyExecutor;
55 import com.jetbrains.edu.learning.checker.StudyTestRunner;
56 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderDeleteHandler;
57 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter;
58 import com.jetbrains.edu.learning.core.EduNames;
59 import com.jetbrains.edu.learning.core.EduUtils;
60 import com.jetbrains.edu.learning.courseFormat.*;
61 import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
62 import com.jetbrains.edu.learning.editor.StudyEditor;
63 import com.jetbrains.edu.learning.ui.StudyToolWindow;
64 import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
65 import com.petebevin.markdown.MarkdownProcessor;
66 import org.jetbrains.annotations.NotNull;
67 import org.jetbrains.annotations.Nullable;
72 import java.util.ArrayList;
73 import java.util.Collection;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.concurrent.Callable;
77 import java.util.concurrent.ExecutionException;
78 import java.util.concurrent.Future;
80 public class StudyUtils {
81 private StudyUtils() {
84 private static final Logger LOG = Logger.getInstance(StudyUtils.class.getName());
85 private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description";
86 private static final String ourPrefix = "<html><head><script type=\"text/x-mathjax-config\">\n" +
87 " MathJax.Hub.Config({\n" +
89 " inlineMath: [ ['$','$'], [\"\\\\(\",\"\\\\)\"] ],\n" +
90 " displayMath: [ ['$$','$$'], [\"\\\\[\",\"\\\\]\"] ],\n" +
91 " processEscapes: true,\n" +
92 " processEnvironments: true\n" +
94 " displayAlign: 'center',\n" +
95 " \"HTML-CSS\": {\n" +
96 " styles: {'#mydiv': {\"font-size\": %s}},\n" +
97 " preferredFont: null,\n" +
98 " linebreaks: { automatic: true }\n" +
101 "</script><script type=\"text/javascript\"\n" +
102 " src=\"http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML-full\">\n" +
103 " </script></head><body><div id=\"mydiv\">";
105 private static final String ourPostfix = "</div></body></html>";
107 public static void closeSilently(@Nullable final Closeable stream) {
108 if (stream != null) {
112 catch (IOException e) {
118 public static boolean isZip(String fileName) {
119 return fileName.contains(".zip");
123 public static <T> T getFirst(@NotNull final Iterable<T> container) {
124 Iterator<T> iterator = container.iterator();
125 if (!iterator.hasNext()) {
128 return iterator.next();
131 public static boolean indexIsValid(int index, @NotNull final Collection collection) {
132 int size = collection.size();
133 return index >= 0 && index < size;
136 @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
138 public static String getFileText(@Nullable final String parentDir, @NotNull final String fileName, boolean wrapHTML,
139 @NotNull final String encoding) {
140 final File inputFile = parentDir != null ? new File(parentDir, fileName) : new File(fileName);
141 if (!inputFile.exists()) return null;
142 final StringBuilder taskText = new StringBuilder();
143 BufferedReader reader = null;
145 reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), encoding));
147 while ((line = reader.readLine()) != null) {
148 taskText.append(line).append("\n");
150 taskText.append("<br>");
153 return wrapHTML ? UIUtil.toHtml(taskText.toString()) : taskText.toString();
155 catch (IOException e) {
156 LOG.info("Failed to get file text from file " + fileName, e);
159 closeSilently(reader);
164 public static void updateAction(@NotNull final AnActionEvent e) {
165 final Presentation presentation = e.getPresentation();
166 presentation.setEnabled(false);
167 final Project project = e.getProject();
168 if (project != null) {
169 final StudyEditor studyEditor = getSelectedStudyEditor(project);
170 if (studyEditor != null) {
171 presentation.setEnabledAndVisible(true);
176 public static void updateToolWindows(@NotNull final Project project) {
177 final StudyToolWindow studyToolWindow = getStudyToolWindow(project);
178 if (studyToolWindow != null) {
179 String taskText = getTaskText(project);
180 if (taskText != null) {
181 studyToolWindow.setTaskText(taskText, null, project);
184 LOG.warn("Task text is null");
186 studyToolWindow.updateCourseProgress(project);
190 public static void initToolWindows(@NotNull final Project project) {
191 final ToolWindowManager windowManager = ToolWindowManager.getInstance(project);
192 windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW).getContentManager().removeAllContents(false);
193 StudyToolWindowFactory factory = new StudyToolWindowFactory();
194 factory.createToolWindowContent(project, windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW));
199 public static StudyToolWindow getStudyToolWindow(@NotNull final Project project) {
200 if (project.isDisposed()) return null;
202 ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
203 if (toolWindow != null) {
204 Content[] contents = toolWindow.getContentManager().getContents();
205 for (Content content: contents) {
206 JComponent component = content.getComponent();
207 if (component != null && component instanceof StudyToolWindow) {
208 return (StudyToolWindow)component;
215 public static void deleteFile(@NotNull final VirtualFile file) {
217 file.delete(StudyUtils.class);
219 catch (IOException e) {
224 public static File copyResourceFile(@NotNull final String sourceName, @NotNull final String copyName, @NotNull final Project project,
225 @NotNull final Task task)
227 final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
228 final Course course = taskManager.getCourse();
229 int taskNum = task.getIndex();
230 int lessonNum = task.getLesson().getIndex();
231 assert course != null;
232 final String pathToResource = FileUtil.join(course.getCourseDirectory(), EduNames.LESSON + lessonNum, EduNames.TASK + taskNum);
233 final File resourceFile = new File(pathToResource, copyName);
234 FileUtil.copy(new File(pathToResource, sourceName), resourceFile);
239 public static Sdk findSdk(@NotNull final Task task, @NotNull final Project project) {
240 final Language language = task.getLesson().getCourse().getLanguageById();
241 return StudyExecutor.INSTANCE.forLanguage(language).findSdk(project);
245 public static StudyTestRunner getTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
246 final Language language = task.getLesson().getCourse().getLanguageById();
247 return StudyExecutor.INSTANCE.forLanguage(language).getTestRunner(task, taskDir);
250 public static RunContentExecutor getExecutor(@NotNull final Project project, @NotNull final Task currentTask,
251 @NotNull final ProcessHandler handler) {
252 final Language language = currentTask.getLesson().getCourse().getLanguageById();
253 return StudyExecutor.INSTANCE.forLanguage(language).getExecutor(project, handler);
256 public static void setCommandLineParameters(@NotNull final GeneralCommandLine cmd,
257 @NotNull final Project project,
258 @NotNull final String filePath,
259 @NotNull final String sdkPath,
260 @NotNull final Task currentTask) {
261 final Language language = currentTask.getLesson().getCourse().getLanguageById();
262 StudyExecutor.INSTANCE.forLanguage(language).setCommandLineParameters(cmd, project, filePath, sdkPath, currentTask);
265 public static void showNoSdkNotification(@NotNull final Task currentTask, @NotNull final Project project) {
266 final Language language = currentTask.getLesson().getCourse().getLanguageById();
267 StudyExecutor.INSTANCE.forLanguage(language).showNoSdkNotification(project);
272 * shows pop up in the center of "check task" button in study editor
274 public static void showCheckPopUp(@NotNull final Project project, @NotNull final Balloon balloon) {
275 final StudyEditor studyEditor = getSelectedStudyEditor(project);
276 assert studyEditor != null;
278 balloon.show(computeLocation(studyEditor.getEditor()), Balloon.Position.above);
279 Disposer.register(project, balloon);
282 public static RelativePoint computeLocation(Editor editor){
284 final Rectangle visibleRect = editor.getComponent().getVisibleRect();
285 Point point = new Point(visibleRect.x + visibleRect.width + 10,
287 return new RelativePoint(editor.getComponent(), point);
292 * returns language manager which contains all the information about language specific file names
295 public static StudyLanguageManager getLanguageManager(@NotNull final Course course) {
296 Language language = course.getLanguageById();
297 return language == null ? null : StudyLanguageManager.INSTANCE.forLanguage(language);
300 public static boolean isTestsFile(@NotNull Project project, @NotNull final String name) {
301 Course course = StudyTaskManager.getInstance(project).getCourse();
302 if (course == null) {
305 StudyLanguageManager manager = getLanguageManager(course);
306 if (manager == null) {
309 return manager.getTestFileName().equals(name);
313 public static TaskFile getTaskFile(@NotNull final Project project, @NotNull final VirtualFile file) {
314 final Course course = StudyTaskManager.getInstance(project).getCourse();
315 if (course == null) {
318 VirtualFile taskDir = file.getParent();
319 if (taskDir == null) {
322 //need this because of multi-module generation
323 if (EduNames.SRC.equals(taskDir.getName())) {
324 taskDir = taskDir.getParent();
325 if (taskDir == null) {
329 final String taskDirName = taskDir.getName();
330 if (taskDirName.contains(EduNames.TASK)) {
331 final VirtualFile lessonDir = taskDir.getParent();
332 if (lessonDir != null) {
333 int lessonIndex = EduUtils.getIndex(lessonDir.getName(), EduNames.LESSON);
334 List<Lesson> lessons = course.getLessons();
335 if (!indexIsValid(lessonIndex, lessons)) {
338 final Lesson lesson = lessons.get(lessonIndex);
339 int taskIndex = EduUtils.getIndex(taskDirName, EduNames.TASK);
340 final List<Task> tasks = lesson.getTaskList();
341 if (!indexIsValid(taskIndex, tasks)) {
344 final Task task = tasks.get(taskIndex);
345 return task.getFile(file.getName());
351 public static void drawAllWindows(Editor editor, TaskFile taskFile) {
352 editor.getMarkupModel().removeAllHighlighters();
353 final Project project = editor.getProject();
354 if (project == null) return;
355 final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
356 for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) {
357 final JBColor color = taskManager.getColor(answerPlaceholder);
358 EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, color);
361 final Document document = editor.getDocument();
362 EditorActionManager.getInstance()
363 .setReadonlyFragmentModificationHandler(document, new EduAnswerPlaceholderDeleteHandler(editor));
364 EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile);
365 editor.getColorsScheme().setColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR, null);
369 public static StudyEditor getSelectedStudyEditor(@NotNull final Project project) {
371 final FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow().
372 getSelectedEditor().getSelectedEditorWithProvider().getFirst();
373 if (fileEditor instanceof StudyEditor) {
374 return (StudyEditor)fileEditor;
377 catch (Exception e) {
384 public static Editor getSelectedEditor(@NotNull final Project project) {
385 final StudyEditor studyEditor = getSelectedStudyEditor(project);
386 if (studyEditor != null) {
387 return studyEditor.getEditor();
392 public static void deleteGuardedBlocks(@NotNull final Document document) {
393 if (document instanceof DocumentImpl) {
394 final DocumentImpl documentImpl = (DocumentImpl)document;
395 List<RangeMarker> blocks = documentImpl.getGuardedBlocks();
396 for (final RangeMarker block : blocks) {
397 ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> document.removeGuardedBlock(block)));
404 public static VirtualFile getPatternFile(@NotNull TaskFile taskFile, String name) {
405 Task task = taskFile.getTask();
406 String lessonDir = EduNames.LESSON + String.valueOf(task.getLesson().getIndex());
407 String taskDir = EduNames.TASK + String.valueOf(task.getIndex());
408 Course course = task.getLesson().getCourse();
409 File resourceFile = new File(course.getCourseDirectory());
410 if (!resourceFile.exists()) {
413 String patternPath = FileUtil.join(resourceFile.getPath(), lessonDir, taskDir, name);
414 VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true);
415 if (patternFile == null) {
422 public static Document getPatternDocument(@NotNull final TaskFile taskFile, String name) {
423 VirtualFile patternFile = getPatternFile(taskFile, name);
424 if (patternFile == null) {
427 return FileDocumentManager.getInstance().getDocument(patternFile);
430 public static boolean isRenameableOrMoveable(@NotNull final Project project, @NotNull final Course course, @NotNull final PsiElement element) {
431 if (element instanceof PsiFile) {
432 VirtualFile virtualFile = ((PsiFile)element).getVirtualFile();
433 if (project.getBaseDir().equals(virtualFile.getParent())) {
436 TaskFile file = getTaskFile(project, virtualFile);
440 String name = virtualFile.getName();
441 return !isTestsFile(project, name) && !isTaskDescriptionFile(name);
443 if (element instanceof PsiDirectory) {
444 VirtualFile virtualFile = ((PsiDirectory)element).getVirtualFile();
445 VirtualFile parent = virtualFile.getParent();
446 if (parent == null) {
449 if (project.getBaseDir().equals(parent)) {
452 Lesson lesson = course.getLesson(parent.getName());
453 if (lesson != null) {
454 Task task = lesson.getTask(virtualFile.getName());
463 public static boolean canRenameOrMove(DataContext dataContext) {
464 Project project = CommonDataKeys.PROJECT.getData(dataContext);
465 PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
466 if (element == null || project == null) {
469 Course course = StudyTaskManager.getInstance(project).getCourse();
470 if (course == null || !EduNames.STUDY.equals(course.getCourseMode())) {
474 if (!isRenameableOrMoveable(project, course, element)) {
481 public static String getTaskTextFromTask(@Nullable final VirtualFile taskDirectory, @Nullable final Task task) {
482 if (task == null || task.getLesson() == null || task.getLesson().getCourse() == null) {
485 final Course course = task.getLesson().getCourse();
486 String text = task.getText() != null ? task.getText() : getTaskTextFromTaskName(taskDirectory, EduNames.TASK_MD);
488 if (text == null) return null;
489 if (course.isAdaptive()) text = wrapAdaptiveCourseText(text);
491 if (text != null && !text.isEmpty()) {
492 return wrapTextToDisplayLatex(text);
494 if (taskDirectory != null) {
495 String fileNameWithoutExtension = FileUtil.getNameWithoutExtension(EduNames.TASK_HTML);
496 int activeStepIndex = task.getActiveSubtaskIndex();
497 if (activeStepIndex != 0) {
498 fileNameWithoutExtension += EduNames.SUBTASK_MARKER + activeStepIndex;
500 final String taskTextFileHtml = getTaskTextFromTaskName(taskDirectory, getTaskDescriptionName(fileNameWithoutExtension, EduNames.TASK_HTML));
501 if (taskTextFileHtml != null) return wrapTextToDisplayLatex(taskTextFileHtml);
503 final String taskTextFileMd = getTaskTextFromTaskName(taskDirectory, getTaskDescriptionName(fileNameWithoutExtension, EduNames.TASK_MD));
504 if (taskTextFileMd != null) return wrapTextToDisplayLatex(convertToHtml(taskTextFileMd));
509 private static String wrapAdaptiveCourseText(@NotNull String text) {
510 return text + "\n\n<b>Note</b>: Use standard input to obtain input for the task.";
514 private static String getTaskDescriptionName(String fileNameWithoutExtension, String defaultName) {
515 return fileNameWithoutExtension + "." + FileUtilRt.getExtension(defaultName);
518 public static String wrapTextToDisplayLatex(String taskTextFileHtml) {
519 final String prefix = String.format(ourPrefix, EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize());
520 return prefix + taskTextFileHtml + ourPostfix;
524 private static String getTaskTextFromTaskName(@Nullable VirtualFile taskDirectory, @NotNull String taskTextFilename) {
525 if (taskDirectory == null) return null;
526 taskDirectory.refresh(false, true);
527 VirtualFile taskTextFile = ObjectUtils.chooseNotNull(taskDirectory.findChild(EduNames.TASK_HTML),
528 taskDirectory.findChild(EduNames.TASK_MD));
529 if (taskTextFile == null) {
530 VirtualFile srcDir = taskDirectory.findChild(EduNames.SRC);
531 if (srcDir != null) {
532 taskTextFile = srcDir.findChild(taskTextFilename);
535 if (taskTextFile != null) {
536 return String.valueOf(LoadTextUtil.loadText(taskTextFile));
542 public static StudyPluginConfigurator getConfigurator(@NotNull final Project project) {
543 StudyPluginConfigurator[] extensions = StudyPluginConfigurator.EP_NAME.getExtensions();
544 for (StudyPluginConfigurator extension: extensions) {
545 if (extension.accept(project)) {
553 public static StudyTwitterPluginConfigurator getTwitterConfigurator(@NotNull final Project project) {
554 StudyTwitterPluginConfigurator[] extensions = StudyTwitterPluginConfigurator.EP_NAME.getExtensions();
555 for (StudyTwitterPluginConfigurator extension: extensions) {
556 if (extension.accept(project)) {
564 public static String getTaskText(@NotNull final Project project) {
565 TaskFile taskFile = getSelectedTaskFile(project);
566 if (taskFile == null) {
567 return EMPTY_TASK_TEXT;
569 final Task task = taskFile.getTask();
571 return getTaskTextFromTask(task.getTaskDir(project), task);
576 public static TaskFile getSelectedTaskFile(@NotNull Project project) {
577 VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles();
578 TaskFile taskFile = null;
579 for (VirtualFile file : files) {
580 taskFile = getTaskFile(project, file);
581 if (taskFile != null) {
589 public static Task getCurrentTask(@NotNull final Project project) {
590 final TaskFile taskFile = getSelectedTaskFile(project);
591 return taskFile != null ? taskFile.getTask() : null;
594 public static boolean isStudyProject(@NotNull Project project) {
595 return StudyTaskManager.getInstance(project).getCourse() != null;
598 public static boolean isStudentProject(@NotNull Project project) {
599 Course course = StudyTaskManager.getInstance(project).getCourse();
600 if (course == null) {
603 return EduNames.STUDY.equals(course.getCourseMode());
607 public static Project getStudyProject() {
608 Project studyProject = null;
609 Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
610 for (Project project : openProjects) {
611 if (StudyTaskManager.getInstance(project).getCourse() != null) {
612 studyProject = project;
619 public static File getCourseDirectory(@NotNull Project project, Course course) {
620 final File courseDirectory;
621 if (course.isAdaptive()) {
622 courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR,
623 StudyProjectGenerator.ADAPTIVE_COURSE_PREFIX + course.getName()
624 + "_" + StudyTaskManager.getInstance(project).getUser().getEmail());
627 courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR, course.getName());
629 return courseDirectory;
632 public static boolean hasJavaFx() {
634 Class.forName("javafx.application.Platform");
637 catch (ClassNotFoundException e) {
643 public static Task getTask(@NotNull Project project, @NotNull VirtualFile taskVF) {
644 Course course = StudyTaskManager.getInstance(project).getCourse();
645 if (course == null) {
648 VirtualFile lessonVF = taskVF.getParent();
649 if (lessonVF == null) {
652 Lesson lesson = course.getLesson(lessonVF.getName());
653 if (lesson == null) {
656 return lesson.getTask(taskVF.getName());
660 public static VirtualFile getTaskDir(@NotNull VirtualFile taskFile) {
661 VirtualFile parent = taskFile.getParent();
662 if (parent == null) {
665 String name = parent.getName();
666 if (name.contains(EduNames.TASK)) {
669 if (EduNames.SRC.equals(name)) {
670 return parent.getParent();
676 public static Task getTaskForFile(@NotNull Project project, @NotNull VirtualFile taskFile) {
677 VirtualFile taskDir = getTaskDir(taskFile);
678 if (taskDir == null) {
681 return getTask(project, taskDir);
684 // supposed to be called under progress
686 public static <T> T execCancelable(@NotNull final Callable<T> callable) {
687 final Future<T> future = ApplicationManager.getApplication().executeOnPooledThread(callable);
689 while (!future.isCancelled() && !future.isDone()) {
690 ProgressManager.checkCanceled();
691 TimeoutUtil.sleep(500);
695 result = future.get();
697 catch (InterruptedException | ExecutionException e) {
698 LOG.warn(e.getMessage());
704 public static Task getTaskFromSelectedEditor(Project project) {
705 final StudyEditor editor = getSelectedStudyEditor(project);
707 if (editor != null) {
708 final TaskFile file = editor.getTaskFile();
709 task = file.getTask();
714 private static String convertToHtml(@NotNull final String content) {
715 ArrayList<String> lines = ContainerUtil.newArrayList(content.split("\n|\r|\r\n"));
716 MarkdownUtil.replaceHeaders(lines);
717 MarkdownUtil.replaceCodeBlock(lines);
719 return new MarkdownProcessor().markdown(StringUtil.join(lines, "\n"));
722 public static boolean isTaskDescriptionFile(@NotNull final String fileName) {
723 if (EduNames.TASK_HTML.equals(fileName) || EduNames.TASK_MD.equals(fileName)) {
726 String extension = FileUtilRt.getExtension(fileName);
727 if (!extension.equals(FileUtilRt.getExtension(EduNames.TASK_HTML)) && !extension.equals(FileUtilRt.getExtension(EduNames.TASK_MD))) {
730 return fileName.contains(EduNames.TASK) && fileName.contains(EduNames.SUBTASK_MARKER);
734 public static VirtualFile findTaskDescriptionVirtualFile(@NotNull Project project, @NotNull VirtualFile taskDir) {
735 Task task = getTask(project, taskDir.getName().contains(EduNames.TASK) ? taskDir: taskDir.getParent());
739 String fileNameWithoutExtension = FileUtil.getNameWithoutExtension(EduNames.TASK_HTML);
740 int activeStepIndex = task.getActiveSubtaskIndex();
741 if (activeStepIndex != 0) {
742 fileNameWithoutExtension += EduNames.SUBTASK_MARKER + activeStepIndex;
744 return ObjectUtils.chooseNotNull(taskDir.findChild(getTaskDescriptionName(fileNameWithoutExtension, EduNames.TASK_HTML)),
745 taskDir.findChild(getTaskDescriptionName(fileNameWithoutExtension, EduNames.TASK_MD)));
749 public static String getTaskDescriptionFileName(final boolean useHtml) {
750 return useHtml ? EduNames.TASK_HTML : EduNames.TASK_MD;
754 public static Document getDocument(String basePath, int lessonIndex, int taskIndex, String fileName) {
755 String taskPath = FileUtil.join(basePath, EduNames.LESSON + lessonIndex, EduNames.TASK + taskIndex);
756 VirtualFile taskFile = LocalFileSystem.getInstance().findFileByPath(FileUtil.join(taskPath, fileName));
757 if (taskFile == null) {
758 taskFile = LocalFileSystem.getInstance().findFileByPath(FileUtil.join(taskPath, EduNames.SRC, fileName));
760 if (taskFile == null) {
763 return FileDocumentManager.getInstance().getDocument(taskFile);
766 public static void showErrorPopupOnToolbar(@NotNull Project project) {
767 final Balloon balloon =
768 JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("Couldn't post your reaction", MessageType.ERROR, null).createBalloon();
769 showCheckPopUp(project, balloon);
772 public static void selectFirstAnswerPlaceholder(@Nullable final StudyEditor studyEditor, @NotNull final Project project) {
773 if (studyEditor == null) return;
774 final Editor editor = studyEditor.getEditor();
775 IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
776 final List<AnswerPlaceholder> placeholders = studyEditor.getTaskFile().getActivePlaceholders();
777 if (placeholders.isEmpty()) return;
778 final AnswerPlaceholder placeholder = placeholders.get(0);
779 int startOffset = placeholder.getOffset();
780 editor.getSelectionModel().setSelection(startOffset, startOffset + placeholder.getRealLength());
783 public static void registerStudyToolWindow(@Nullable final Course course, Project project) {
784 if (course != null && "PyCharm".equals(course.getCourseType())) {
785 final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
786 registerToolWindows(toolWindowManager, project);
787 final ToolWindow studyToolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
788 if (studyToolWindow != null) {
789 studyToolWindow.show(null);
790 initToolWindows(project);
795 private static void registerToolWindows(@NotNull final ToolWindowManager toolWindowManager, Project project) {
796 final ToolWindow toolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
797 if (toolWindow == null) {
798 toolWindowManager.registerToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW, true, ToolWindowAnchor.RIGHT, project, true);